You are on page 1of 877

pr

1?
1I

I
Bernd Bruegge, Allen H. Dutoit

Inżynieria
oprogramowania
w ujęciu
obiektowym
U ML, wzorce projektowe i Java

Sprawdź, jak sprawnie i bezbłędnie projektować systemy informatyczne!


Czym jest inżynieria oprogramowania?
Jak zapanować nad wszystkimi aspektami procesu projektowania?
Jak wygląda cykl życia oprogramowania?

Helion
Dla Goega, Toby'ego i Clary.
-B.B
Dla mojej kochanej rodziny:
Vicky, Eleni, Anny Marii, Michelle i Chrisa.
— A.H.D
Spis treści

Przedmowa 17
Wstęp 19
Podziękowania 31

CZĘŚĆ I Zaczynamy 33

Rozdział 1. Wprowadzenie do inżynierii oprogramowania 35


1.1. Wprowadzenie: niepowodzenia w inżynierii oprogramowania 36
1.2. Czym jest inżynieria oprogramowania? 38
1.2.1. Modelowanie 38
1.2.2. Rozwiązywanie problemów 40
1.2.3. Pozyskiwanie wiedzy 41
1.2.4. Racjonalizacja 42
1.3. Podstawowe koncepcje inżynierii oprogramowania 43
1.3.1. Uczestnicy i role 44
1.3.2. Systemy i modele 46
1.3.3. Produkty 46
1.3.4. Aktywności, zadania i zasoby 47
1.3.5. Wymagania funkcyjne i pozafunkcyjne 48
1.3.6. Notacje, metody i metodologie 48
1.4. Aktywności inżynierii oprogramowania 49
1.4.1. Zbieranie wymagań 50
1.4.2. Analiza 51
1.4.3. Projekt systemu 51
1.4.4. Projektowanie obiektów 53
1.4.5. Implementowanie 53
1.4.6. Testowanie 54
1.5. Zarządzanie tworzeniem oprogramowania 54
1.5.1. Komunikacja 55
1.5.2. Zarządzanie racjonalizacją 55
1.5.3. Zarządzanie konfiguracją oprogramowania 56
1.5.4. Zarządzanie projektem 56
1.5.5. Cykl życiowy oprogramowania 56
1.5.6. Podsumowanie 57
6 Spis treści

1.6. Analiza przypadku — system ARENA 57


1.7. Literatura uzupełniająca 58
1.8. Ćwiczenia 59

Rozdział 2. Modelowanie w języku UML 63


2.1. Wprowadzenie 64
2.2. Ogólnie o UML 65
2.2.1. Diagramy przypadków użycia 65
2.2.2. Diagramy klas 65
2.2.3. Diagramy interakcji 67
2.2.4. Diagram stanów 67
2.2.5. Diagramy aktywności 68
2.3. Podstawowe koncepcje modelowania 69
2.3.1. Systemy, modele i widoki 69
2.3.2. Typy danych, abstrakcyjne typy danych i instancje 72
2.3.3. Klasy, klasy abstrakcyjne i obiekty 73
2.3.4. Klasy zdarzeniowe, zdarzenia i komunikaty 75
2.3.5. Modelowanie zorientowane obiektowo 76
2.3.6. Falsyfikacja i prototypowanie 77
2.4. UML — głębszy wgląd 78
2.4.1. Diagramy przypadków użycia 79
2.4.2. Diagramy klas 86
2.4.3. Diagramy interakcji 95
2.4.4. Diagramy stanów 98
2.4.5. Diagramy aktywności 101
2.4.6. Organizacja diagramów 104
2.4.7. Rozszerzenia diagramów 106
2.5. Literatura uzupełniająca 107
2.6. Ćwiczenia 108

Rozdział 3. Organizacja projektu i komunikacja 113


3.1. Wstęp — katastrofa Ariane 114
3.2. O projekcie ogólnie 115
3.3. Koncepcje organizacyjne projektu 119
3.3.1. Organizacja projektów 119
3.3.2. Role w realizacji projektu 122
3.3.3. Zadania i produkty 124
3.3.4. Harmonogramy 126
3.4. Koncepcje komunikacyjne projektu 128
3.4.1. Komunikacja planowa 128
3.4.2. Komunikacja pozaplanowa 135
3.4.3. Mechanizmy komunikacyjne 138
3.5. Aktywności organizacyjne 146
3.5.1. Dołączanie do zespołu 146
3.5.2. Dołączanie do infrastruktury komunikacyjnej 146
7 Spis treści

3.5.3. Udział w zebraniach zespołu 147


3.5.4. Organizacja przeglądów 149
3.6. Literatura uzupełniająca 151
3.7. Ćwiczenia 152

CZĘŚĆ II Zmagania ze złożonością 155

Rozdział 4. Zbieranie wymagań 157


4.1. Wstęp: przykłady problemów z użytecznością 158
4.2. O zbieraniu wymagań ogólnie 159
4.3. Koncepcje zbierania wymagań 161
4.3.1. Wymagania funkcyjne 161
4.3.2. Wymagania pozafunkcyjne 162
4.3.3. Kompletność, spójność, jednoznaczność i poprawność 164
4.3.4. Realizm, weryfikowalność i identyfikowalność 165
4.3.5. Inżynieria pierwotna, inżynieria wtórna
i inżynieria interfejsu 165
4.4. Aktywności związane ze zbieraniem wymagań 166
4.4.1. Identyfikacja aktorów 167
4.4.2. Identyfikacja scenariuszy 169
4.4.3. Identyfikacja przypadków użycia 171
4.4.4. Doskonalenie przypadków użycia 173
4.4.5. Identyfikacja relacji między aktorami
a przypadkami użycia 176
4.4.6. Początkowa identyfikacja obiektów
modelu analitycznego 179
4.4.7. Identyfikacja wymagań pozafunkcyjnych 182
4.5. Zarządzanie zbieraniem wymagań 183
4.5.1. Negocjowanie specyfikacji z klientem:
metoda Joint Application Design 185
4.5.2. Zarządzanie identyfikowalnością 187
4.5.3. Dokumentowanie zbierania wymagań 188
4.6. Analiza przypadku — system ARENA 190
4.6.1. Wstępna deklaracja problemu 190
4.6.2. Identyfikacja aktorów i scenariuszy 192
4.6.3. Identyfikacja przypadków użycia 195
4.6.4. Doskonalenie przypadków użycia
i identyfikacja relacji 198
4.6.5. Identyfikacja wymagań pozafunkcyjnych 204
4.6.6. Wnioski 204
4.7. Literatura uzupełniająca 205
4.8. Ćwiczenia 207
8 Spis treści

Rozdział 5. Analiza wymagań 211


5.1. Wstęp: złudzenie optyczne 212
5.2. O analizie wymagań ogólnie 212
5.3. Koncepcje analizy wymagań 214
5.3.1. Analityczny model obiektowy i modele dynamiczne 214
5.3.2. Obiekty encji, obiekty brzegowe i obiekty sterujące 215
5.3.3. Generalizacja i specjalizacja 216
5.4. Aktywności analizy wymagań:
od przypadków użycia do obiektów 217
5.4.1. Identyfikacja obiektów encji 218
5.4.2. Identyfikacja obiektów brzegowych 220
5.4.3. Identyfikacja obiektów sterujących 222
5.4.4. Odwzorowywanie przypadków użycia w obiekty
za pomocą diagramów sekwencji 224
5.4.5. Modelowanie interakcji między obiektami
za pomocą kart CRC 228
5.4.6. Identyfikacja skojarzeń 228
5.4.7. Identyfikacja agregacji 231
5.4.8. Identyfikacja atrybutów 232
5.4.9. Modelowanie zachowania poszczególnych obiektów
uzależnionego od ich stanu 233
5.4.10. Modelowanie relacji dziedziczenia między obiektami 234
5.4.11. Przeglądy modelu analitycznego 235
5.4.12. Podsumowanie analizy 236
5.5. Zarządzanie analizą wymagań 237
5.5.1. Dokumentowanie analizy wymagań 238
5.5.2. Przydzielanie odpowiedzialności 239
5.5.3. Komunikacja w związku z analizą wymagań 240
5.5.4. Iteracje modelu analitycznego 241
5.5.5. Uzgodnienie modelu analitycznego z klientem 243
5.6. Analiza przypadku — system ARENA 245
5.6.1. Identyfikacja obiektów encji 245
5.6.2. Identyfikacja obiektów brzegowych 250
5.6.3. Identyfikacja obiektów sterujących 251
5.6.4. Modelowanie interakcji między obiektami 252
5.6.5. Weryfikacja i konsolidacja modelu analitycznego 254
5.6.6. Wnioski 256
5.7. Literatura uzupełniająca 258
5.8. Ćwiczenia 258

Rozdział 6. Projektowanie systemu — dekompozycja na podsystemy 263


6.1. Wstęp: projekt mieszkania 264
6.2. O projektowaniu systemu ogólnie 266
6.3. Koncepcje projektowania systemu 267
9 Spis treści

6.3.1. Podsystemy i klasy 268


6.3.2. Usługi i interfejsy podsystemów 270
6.3.3. Sprzężenie i spoistość 271
6.3.4. Warstwy i partycje 275
6.3.5. Style architektoniczne 279
6.4. Aktywności projektowania systemu:
od obiektów do podsystemów 288
6.4.1. Punkt wyjścia: model analityczny systemu
planowania podróży 288
6.4.2. Identyfikowanie celów projektowych 290
6.4.3. Identyfikowanie podsystemów 294
6.5. Literatura uzupełniająca 296
6.6. Ćwiczenia 297

Rozdział 7. Projekt systemu: realizacja celów projektowych 301


7.1. Wstęp: przykład redundancji 302
7.2. O aktywnościach projektowania systemu ogólnie 303
7.3. Koncepcje: diagramy wdrażania UML 304
7.4. Aktywności realizacji celów projektowych 306
7.4.1. Odwzorowywanie podsystemów
w procesory i komponenty 306
7.4.2. Identyfikowanie trwałych danych i ich przechowywanie 309
7.4.3. Definiowanie założeń kontroli dostępu 312
7.4.4. Projektowanie globalnego przepływu sterowania 319
7.4.5. Identyfikowanie usług 321
7.4.6. Identyfikowanie warunków granicznych 323
7.4.7. Weryfikowanie projektu systemu 326
7.5. Zarządzanie projektowaniem systemu 328
7.5.1. Dokumentowanie projektu systemu 328
7.5.2. Przydzielanie odpowiedzialności 330
7.5.3. Komunikacja w projektowaniu systemu 331
7.5.4. Iteracje projektowania systemu 333
7.6. Analiza przypadku — system ARENA 334
7.6.1. Identyfikowanie celów projektowych 335
7.6.2. Identyfikowanie podsystemów 336
7.6.3. Odwzorowanie podsystemów w procesory
i komponenty 337
7.6.4. Identyfikowanie i przechowywanie trwałych danych 339
7.6.5. Definiowanie założeń kontroli dostępu 340
7.6.6. Projektowanie globalnego przepływu sterowania 341
7.6.7. Identyfikowanie usług 343
7.6.8. Identyfikowanie warunków granicznych 345
7.6.9. Wnioski 347
7.7. Literatura uzupełniająca 348
7.8. Ćwiczenia 348
10 Spis treści

Rozdział 8. Projektowanie obiektów:


wielokrotne wykorzystywanie rozwiązań wzorcowych 353
8.1. Wstęp: wpadki produkcyjne 354
8.2. O projektowaniu obiektów ogólnie 355
8.3. Koncepcja wielokrotnego wykorzystywania
— dziedziczenie, delegowanie i wzorce projektowe 359
8.3.1. Obiekty aplikacyjne i obiekty realizacyjne 359
8.3.2. Dziedziczenie implementacyjne
i dziedziczenie specyfikacyjne 360
8.3.3. Delegowanie 363
8.3.4. Zasada zastępowania Liskov 364
8.3.5. Delegowanie i dziedziczenie
we wzorcach projektowych 364
8.4. Wybór wzorców projektowych i gotowych komponentów 367
8.4.1. Hermetyzacja przechowywania danych
za pomocą wzorca projektowego Most 368
8.4.2. Hermetyzacja niekompatybilnych komponentów
za pomocą wzorca projektowego Adapter 371
8.4.3. Hermetyzacja kontekstu za pomocą
wzorca projektowego Strategia 373
8.4.4. Hermetyzacja platformy za pomocą
wzorca projektowego Fabryka abstrakcyjna 376
8.4.5. Hermetyzacja przepływu sterowania za pomocą
wzorca projektowego Polecenie 377
8.4.6. Hermetyzacja hierarchii za pomocą
wzorca projektowego Kompozyt 378
8.4.7. Heurystyki wyboru wzorców projektowych 379
8.4.8. Identyfikowanie i przystosowywanie
frameworków aplikacyjnych 381
8.5. Zarządzanie wykorzystywaniem gotowych rozwiązań 386
8.5.1. Dokumentowanie wykorzystywania
gotowych rozwiązań 388
8.5.2. Przydzielanie odpowiedzialności 389
8.6. Analiza przypadku — system ARENA 390
8.6.1. Zastosowanie wzorca projektowego
Fabryka abstrakcyjna 390
8.6.2. Zastosowanie wzorca projektowego Polecenie 392
8.6.3. Zastosowanie wzorca projektowego Obserwator 393
8.6.4. Wnioski 393
8.7. Literatura uzupełniająca 394
8.8. Ćwiczenia 395
11 Spis treści

Rozdział 9. Projektowanie obiektów: specyfikowanie interfejsów 399


9.1. Wstęp: kolej miejska i tramwaje 400
9.2. O specyfikowaniu interfejsów ogólnie 401
9.3. Koncepcje specyfikowania interfejsów 403
9.3.1. Implementator, ekstender i użytkownik klasy 403
9.3.2. Typy, sygnatury i widzialność 403
9.3.3. Kontrakty: niezmienniki, warunki wstępne
i warunki końcowe 406
9.3.4. Język OCL (Object Constraint Language) 407
9.3.5. Kolekcje OCL: zbiory, wielozbiory i ciągi 411
9.3.6. Kwantyfikatory OCL: forAll() i exists() 415
9.4. Aktywności specyfikowania interfejsów 416
9.4.1. Identyfikowanie brakujących atrybutów i operacji 417
9.4.2. Specyfikowanie typów, sygnatur i widzialności 418
9.4.3. Specyfikowanie warunków wstępnych
i warunków końcowych 419
9.4.4. Specyfikowanie niezmienników 421
9.4.5. Dziedziczenie kontraktów 424
9.5. Zarządzanie projektowaniem obiektów 425
9.5.1. Dokumentowanie projektowania obiektów 425
9.5.2. Przydzielanie odpowiedzialności 431
9.5.3. Wykorzystywanie kontraktów w analizie wymagań 432
9.6. Analiza przypadku — system ARENA 433
9.6.1. Identyfikowanie brakujących operacji
w klasach TournamentStyle i Round 434
9.6.2. Specyfikowanie kontraktów dla klas
TournamentStyle i Round 435
9.6.3. Specyfikowanie kontraktów dla klas KnockOutStyle
i KnockOutRound 438
9.6.4. Wnioski 439
9.7. Literatura uzupełniająca 440
9.8. Ćwiczenia 440

Rozdział 10. Odwzorowywanie modelu na kod 445


10.1. Wstęp: Władca Pierścieni 446
10.2. O odwzorowywaniu ogólnie 447
10.3. Koncepcje odwzorowywania 448
10.3.1. Transformowanie modelu 449
10.3.2. Refaktoryzacja 450
10.3.3. Inżynieria postępująca 452
10.3.4. Inżynieria odwracająca 452
10.3.5. Zasady transformacji 453
10.4. Aktywności odwzorowywania 454
10.4.1. Optymalizowanie modelu obiektowego 455
10.4.2. Odwzorowywanie skojarzeń na kolekcje 458
12 Spis treści

10.4.3. Odwzorowywanie kontraktów w wyjątki 465


10.4.4. Odwzorowywanie modelu obiektowego
w schematy bazy danych 469
10.5. Zarządzanie transformacjami 475
10.5.1. Dokumentowanie transformacji 475
10.5.2. Przydzielanie odpowiedzialności 477
10.6. Analiza przypadku — system ARENA 478
10.6.1. Statystyki systemu ARENA 478
10.6.2. Odwzorowywanie skojarzeń na kolekcje 480
10.6.3. Odwzorowywanie kontraktów w wyjątki 482
10.6.4. Odwzorowywanie modelu obiektowego
w schemat bazy danych 484
10.6.5. Wnioski 485
10.7. Literatura uzupełniająca 485
10.8. Ćwiczenia 486

Rozdziałll. Testowanie 491


11.1. Wstęp: testowanie wahadłowców 492
11.2. O testowaniu ogólnie 494
11.3. Koncepcje związane z testowaniem 498
11.3.1. Usterki, błędne stany i awarie 500
11.3.2. Przypadki testowe 503
11.3.3. Namiastki testowe i sterowniki testowe 505
11.3.4. Poprawki 505
11.4. Aktywności związane z testowaniem 506
11.4.1. Inspekcja komponentu 507
11.4.2. Testowanie użyteczności 508
11.4.3. Testowanie jednostkowe 510
11.4.4. Testowanie integracyjne 519
11.4.5. Testowanie systemu 526
11.5. Zarządzanie testowaniem 531
11.5.1. Planowanie testów 532
11.5.2. Dokumentowanie testowania 532
11.5.3. Przydzielanie odpowiedzialności 536
11.5.4. Testowanie regresyjne 537
11.5.5. Automatyzacja testowania 538
11.5.6. Testowanie bazujące na modelach 539
11.6. Literatura uzupełniająca 541
11.7. Ćwiczenia 543

CZĘŚĆ III Zarządzanie zmianami 547

Rozdział 12. Zarządzanie racjonalizacją 549


12.1. Wstęp: przycinanie wędzonej szynki 550
12.2. O racjonalizacji ogólnie 551
13 Spis treści

12.3. Koncepcje racjonalizacji 554


12.3.1. CTC — system centralnego sterowania ruchem 555
12.3.2. Definiowanie problemów: zagadnienia 556
12.3.3. Eksploracja przestrzeni rozwiązań: propozycje 557
12.3.4. Wartościowanie elementów przestrzeni rozwiązań:
kryteria i argumenty 559
12.3.5. Kolapsacja przestrzeni rozwiązań: rozstrzygnięcie 560
12.3.6. Implementowanie rozstrzygnięć: elementy działania 561
12.3.7. Przykłady modeli zagadnień i ich realizacje 562
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 567
12.4.1. Projekt systemu CTC 568
12.4.2. Kolekcjonowanie racjonalizacji w ramach zebrań 569
12.4.3. Asynchroniczne kolekcjonowanie racjonalizacji 577
12.4.4. Racjonalizacja dyskutowanych zmian 579
12.4.5. Rekonstruowanie racjonalizacji 582
12.5. Kierownicze aspekty zarządzania racjonalizacją 585
12.5.1. Dokumentowanie racjonalizacji 585
12.5.2. Przypisywanie odpowiedzialności 587
12.5.3. Heurystyki komunikowania racjonalizacji 588
12.5.4. Modelowanie i negocjowanie zagadnień 589
12.5.5. Strategie rozwiązywania konfliktów 590
12.6. Literatura uzupełniająca 592
12.7. Ćwiczenia 593

Rozdział 13. Zarządzanie konfiguracją 597


13.1. Wstęp: samoloty 598
13.2. O zarządzaniu konfiguracją ogólnie 600
13.3. Koncepcje zarządzania konfiguracją 602
13.3.1. Elementy konfiguracji i agregaty CM 603
13.3.2. Wersje i konfiguracje 604
13.3.3. Żądania zmian 604
13.3.4. Promocje i emisje 605
13.3.5. Repozytoria i przestrzenie robocze 606
13.3.6. Schematy identyfikowania wersji 606
13.3.7. Zmiany i zbiory zmian 608
13.3.8. Narzędzia wspomagające zarządzanie konfiguracją 610
13.4. Aktywności tworzące zarządzanie konfiguracją 611
13.4.1. Identyfikowanie elementów konfiguracji
i agregatów CM 613
13.4.2. Zarządzanie promocjami 615
13.4.3. Zarządzanie emisjami 616
13.4.4. Zarządzanie gałęziami 619
13.4.5. Zarządzanie wariantami 623
13.4.6. Zarządzanie propozycjami zmian
i ich implementowaniem 626
14 Spis treści

13.5. Kierownicze aspekty zarządzania konfiguracją 627


13.5.1. Dokumentowanie zarządzania konfiguracją 627
13.5.2. Przypisywanie odpowiedzialności 628
13.5.3. Planowanie aktywności
w ramach zarządzania konfiguracją 629
13.5.4. Integracja ciągła jako optymalizacja
zarządzania promocjami i ich testowaniem 630
13.6. Literatura uzupełniająca 632
13.7. Ćwiczenia 633

Rozdział 14. Zarządzanie projektem 637


14.1. Wstęp: uruchomienie misji STS-51L 638
14.2. O zarządzaniu projektem ogólnie 639
14.3. Koncepcje zarządzania projektem 646
14.3.1. Zadania i aktywności 646
14.3.2. Produkty, pakiety pracy i role 646
14.3.3. Struktura podziału pracy 648
14.3.4. Model zadań 649
14.3.5. Macierz kwalifikacji 650
14.3.6. Plan zarządzania projektem 651
14.4. Aktywności klasycznego zarządzania projektem 653
14.4.1. Planowanie projektu 654
14.4.2. Organizowanie projektu 659
14.4.3. Kontrolowanie projektu 665
14.4.4. Kończenie projektu 671
14.5. Aktywności „zwinnej" realizacji projektu 673
14.5.1. Planowanie projektu:
wykazy zaległości produktu i przebiegu 674
14.5.2. Organizowanie projektu 675
14.5.3. Kontrolowanie projektu:
dni robocze i wykresy wygaszania 675
14.5.4. Kończenie projektu: przeglądy przebiegów 677
14.6. Literatura uzupełniająca 677
14.7. Ćwiczenia 679

Rozdział 15. Cykl życiowy oprogramowania 683


15.1. Wstęp: nawigacja polinezyjska 684
15.2. IEEE 1074: standard cykli życiowych 688
15.2.1. Procesy i aktywności 688
15.2.2. Modelowanie cyklu życiowego 690
15.2.3. Zarządzanie projektem 690
15.2.4. Prerealizacja 691
15.2.5. Realizacja — tworzenie systemu 692
15.2.6. Postrealizacja 693
15.2.7. Procesy integralne (międzyrealizacyjne) 694
15 Spis treści

15.3. Charakteryzowanie dojrzałości modeli cyklu życiowego 695


15.4. Modele cyklu życiowego 698
15.4.1. Sekwencyjne modele ukierunkowane na aktywności 699
15.4.2. Iteracyjne modele ukierunkowane na aktywności 701
15.4.3. Modele ukierunkowane na encje 706
15.5. Literatura uzupełniająca 709
15.6. Ćwiczenia 710

Rozdział 16. Wszystko razem, czyli metodologie 713


16.1. Wstęp: pierwsze zdobycie K2 714
16.2. Środowisko projektu 717
16.3. Zagadnienia metodologiczne 719
16.3.1. Ile planowania? 719
16.3.2. Ile powtarzalności? 720
16.3.3. Ile modelowania? 721
16.3.4. Ile procesów cyklu życiowego? 723
16.3.5. Ile kontroli i monitorowania? 723
16.3.6. Kiedy przedefiniować cele projektu? 724
16.4. Spektrum metodologii 724
16.4.1. Metodologia Royce'a 725
16.4.2. Programowanie ekstremalne (XP) 731
16.4.3. Metodologie rugby 737
16.5. Analizy przypadku 742
16.5.1. Projekt XP: ATRACT 743
16.5.2. Lokalny klient: FRIEND 746
16.5.3. Rozproszony projekt: JAMES 754
16.5.4. Podsumowanie analiz przypadku 761
16.6. Literatura uzupełniająca 766
16.7. Ćwiczenia 766

Dodatki 769

Dodatek A Wzorce projektowe 771


A. 1. Fabryka abstrakcyjna (Abstract Factory)
— hermetyzacja platformy 772
A.2. Adapter (Adapter) — otoczka dla starszego kodu 773
A.3. Most (Bridge) — podmiana implementacji 774
A.4. Polecenie (Command) — hermetyzacja przepływu sterowania 775
A.5. Kompozyt (Composite) — rekurencyjna reprezentacja hierarchii 776
A.6. Fasada (Facade) — hermetyzacja podsystemów 777
A.7. Obserwator (Observer) — oddzielenie encji od widoków 778
A.8. Proxy (Proxy) — hermetyzacja kosztownych obiektów 779
A.9. Strategia (Strategy) — hermetyzacja algorytmów 780
A. 10. Heurystyki pomocne w wyborze wzorców projektowych 781
16 Spis treści

Dodatek B Objaśnienia haseł 783


B.l. Terminologia 783
B.2. Słownik terminów angielskich 817

Dodatek C Bibliografia 831


Skorowidz 847
Przedmowa

Jakieś dziesięć lat temu dowiedziałem się o kursie z zakresu inżynierii oprogramowania,
prowadzonym przez Bernda Bruegge'a na Uniwersytecie Carnegie Mellon. Większość prowa-
dzonych przez inne uniwersytety kursów o tej tematyce realizowana jest zwykle w ten sposób,
że każda z niewielkich — trzy- lub czteroosobowych — grup otrzymuje do rozwiązania kon-
kretny problem, w określonym czasie, powiedzmy, jednego miesiąca. I ciąg dalszy zazwyczaj
wygląda tak, że jeden „wiodący" programista narzuca swą koncepcję całemu zespołowi — nie
ma mowy o praktykowaniu umiejętności w zakresie sprawnej komunikacji, o rozwiązywaniu
wątpliwości i niejasności projektowych, niepotrzebne stają się narzędzia do modelowania. Za-
danie do rozwiązania jest wszak dość proste — by nie powiedzieć: rozrywkowe — i tego typu
quasi-profesjonalne praktyki mają nawet duże szansę powodzenia. W rezultacie jednak studenci
kończący semestralny kurs pozostają nieprzygotowani do zmagania się ze złożonością praw-
dziwych wyzwań, jakie wkrótce postawią przed nimi rzeczywiste problemy projektowe.
Na kursie Bruegge'a było jednak inaczej: cała klasa zaangażowana była w realizację
wspólnego projektu, jakim było stworzenie opartego na kwerendach systemu nawigacji dla
miasta Pittsburgh. W projekcie tym wykorzystywano interaktywny system map, zrealizowany
w ramach podobnego kursu w poprzednim semestrze; system ten, opracowany na użytek miej-
scowego departamentu planowania i służb zarządzających lokalnym portem lotniczym miał
jednak tę bolączkę, że dane geograficzne oraz rozkłady jazdy lokalnych autobusów nie dość,
że najeżone były błędami ortograficznymi, to na dodatek przechowywane były w kilku nie-
zgodnych ze sobą formatach. Studenci jednak poradzili sobie z tymi niedostatkami, tworząc
w ciągu semestru bardzo dobry projekt o rozmiarze kodu źródłowego w granicach 27 000 wier-
szy — cóż za znakomita różnica w porównaniu ze wspomnianą powyżej rutyną „rozrywko-
wych" projektów! I choć projektom tej skali daleko jeszcze do wielu olbrzymich przedsięwzięć
programistycznych, to skala ta jest całkowicie wystarczająca, by docenić znaczenie strategii,
organizacji i narzędzi w trudach zmagania się z zawiłościami i złożonością otaczającego nas
świata. Studenci zgłębiali tajniki inżynierii oprogramowania w taki sposób — jedyny skuteczny —
w jaki uczy się każdego rzemiosła: przez praktykę na bazie realnego świata.
Treść tej książki stanowi odzwierciedlenie owej pragmatycznej filozofii tworzenia opro-
gramowania, postrzeganego jako dyscyplina inżynierska. Autorzy prezentują tę skomplikowaną
tematykę w bardzo przystępnym ujęciu, ułatwiającym zrozumienie nawet najtrudniejszych jej
aspektów — w ujęciu zorientowanym obiektowo, z wykorzystaniem UML. Czytelnicy znajdą
tu obszerną dyskusję na temat zarówno technik modelowania, jak i rozwijania umiejętności
w zakresie komunikacji interpersonalnej, tak istotnej dla osiągnięcia sukcesu. Kilka rozdziałów
18 Przedmowa

poświęconych jest też problematyce zarządzania zmianami — często lekceważonej i niedoce-


nianej, choć w istocie towarzyszącej każdemu niemal niebanalnemu projektowi. Gdy się w to
wszystko wczytać, docenia się z należytym respektem i bogactwo, i niesamowicie skompliko-
wany charakter inżynierii oprogramowania.
Osobiście zbudowany jestem błyskotliwymi anegdotami urozmaicającymi treść książki.
Dostarczają one zaskakujących nieraz analogii, nawiązujących do wielkich i małych — często
subtelnych — problemów, jakim stawiać musi nieustannie czoła profesjonalny programista.
Gdy co rusz znajduje się interesujące historie opisujące prymitywne techniki nawigacyjne
Polinezyjczyków, perypetie towarzyszące pierwszemu wydaniu Władcy Pierścieni Tolkiena czy
też zaskakujące wyjaśnienie przyczyny, dla której przycina się szynkę na końcach przed jej wę-
dzeniem — wszystko to, a jakże, a propos zasadniczej tematyki — książka staje się już nie tylko
cenną pozycją w bibliotece profesjonalisty, ale także wciągającą lekturą.

Jim Rumbaugh
Wstęp

W zachodnich Himalajach, w paśmie Karakorum, majestatycznie wznosi się na wyso-


kość 8611 m drugi co do wysokości szczyt Ziemi — K2. Przez himalaistów uważany jest za
najtrudniejszy do zdobycia ośmiotysięcznik. Wyprawa na K2 trwa kilka miesięcy, zazwyczaj
latem, gdy pogoda jest najbardziej sprzyjająca — choć nawet wówczas zamiecie śnieżne nie
należą tu do rzadkości. Uczestnicy wyprawy taszczą ze sobą tysiące kilogramów wyposażenia
— sprzęt wspinaczkowy, stosowne odzienie, namioty, żywność, aparaturę telekomunikacyjną
i setki sztuk innego rozmaitego bagażu. Zaplanowanie takiej wyprawy wymaga sporo czasu
i wysiłku, nie może odbyć się bez udziału dziesiątek pomocników. I najstaranniej nawet za-
projektowana ekspedycja musi nieuchronnie zmagać się z nagłymi i nieprzewidywalnymi wy-
padkami, jak lawiny, awarie sprzętu, zasłabnięcia i inne przypadłości zdrowotne — a to wymaga
zdolności do szybkiego przystosowywania się do nowych sytuacji, poszukiwania doraźnych
rozwiązań, a w skrajnych wypadkach rezygnacji ze szczytnego (nomen omen) celu. Na pod-
stawie dotychczasowej historii szansę powodzenia takiej wyprawy ocenia się na mniej niż 40%.
Ruch lotniczy na terenie USA monitorowany jest i zarządzany poprzez System Krajowej
Przestrzeni Powietrznej (NAS — National Airspace System). System ten obejmuje 18 300 por-
tów lotniczych, 21 centrów sterowania ruchem i ponad 460 wież kontrolnych. Do tego należy
dodać ponad 34 000 urządzeń pomocniczych: radarów, przełączników telekomunikacyjnych,
przekaźników radiowych, systemów komputerowych i wyświetlaczy. Zarządzająca tym wszyst-
kim infrastruktura starzeje się, niestety, w szybkim tempie — dość wspomnieć, iż praca
wzmiankowanych centrów sterowania opiera się na komputerach mainframe serii IBM 3083,
wywodzących się jeszcze z początku lat 80. ubiegłego wieku. W 1996 roku rząd USA zapocząt-
kował program modernizacji infrastruktury NAS, obejmujący między innymi zastosowanie
nawigacji satelitarnej, cyfrowej komunikacji pilotów z kontrolerami ruchu i generalną auto-
matyzację wspomagania procesów decyzyjnych dotyczących wyboru tras, ustalania kolejności
lądowania i tym podobnych. Jest zrozumiałe, iż tak drastyczna modernizacja nie może mieć
wymiaru jednoaktowego, lecz dokonywać się musi w sposób przyrostowy. A to oznacza, że
wprowadzając do infrastruktury nowe komponenty, należy zapewnić nie tylko ich niezawodne
działanie, lecz także niezawodną współpracę z istniejącymi komponentami — i tak na przykład
komunikacja między centrami sterowania a pilotami musi jeszcze przez jakiś czas odbywać się
za pośrednictwem kanałów dwu generacji: analogowych i cyfrowych. Co więcej, wszystko to
odbywać się musi w kontekście gwałtownego wzrostu dynamiki globalnej komunikacji lotniczej,
której rozwój na przestrzeni najbliższych 1 0 - 1 5 lat można co najwyżej w przybliżeniu pro-
gnozować. Nie można zapominać, że poprzednie wysiłki administracji USA w tym kierunku
nie zostały uwieńczone powodzeniem — projekt systemu o nazwie Advanced Automation
20 Wstęp

System (AAS) zawieszony został w roku 1994, czego bezpośrednią przyczyną okazały się pro-
blemy z oprogramowaniem, objawiające się przekraczaniem założonych harmonogramów
0 miesiące i lata oraz nadprogramowymi wydatkami budżetowymi sięgającymi miliardów
dolarów.
Oba przytoczone przykłady ilustrują sytuacje, gdy funkcjonowanie i tak już skompliko-
wanych systemów zderza się z nieprzewidywalnymi zmianami i powinno wyjść z tej konfron-
tacji obronną ręką. Wyzwania, jakie kreuje sama złożoność systemów, ewidentnie przekraczają
możliwości pojedynczego człowieka, a pojawiające się dodatkowo nowe okoliczności zmuszają
do wyjścia poza (niewystarczające już) rutynowe rozwiązania i poszukiwanie ad hoc nowych,
adekwatnych do nowej sytuacji. Wymaga to sprawnego współdziałania wszystkich uczestników
przedsięwzięcia — porażka w tym względzie przekłada się w prostej konsekwencji na klęskę
całego projektu.
Celem tej książki jest dostarczenie czytelnikom wiedzy o tym, jak skutecznie radzić
sobie z takimi wyzwaniami.

Temat

Dziedzina aplikacyjna przedsięwzięcia (planowanie ekspedycji wysokogórskiej, sterowanie


ruchem lotniczym, obsługa transakcji finansowych, przetwarzanie tekstów) obejmuje zwykle
multum różnych koncepcji, z których większość obca jest projektantom realizującym jego
stronę informatyczną. Z kolei dziedzina realizacyjna (a więc całokształt technik programi-
stycznych, narzędzia do projektowania interfejsu użytkownika, środki komunikacji bezprze-
wodowej, oprogramowanie pośredniczące, systemy zarządzania bazami danych, systemy
przetwarzania transakcyjnego, urządzenia mobilne) okazuje się zwykle niedojrzała (lub nie
w pełni dojrzała) do należytego odzwierciedlenia dziedziny aplikacyjnej, chociaż daje projek-
tantom i programistom szeroki wachlarz technologii implementacyjnych, często konkuren-
cyjnych względem siebie. W konsekwencji tego stanu rzeczy sam system staje się niezwykle
złożony już na etapie wstępnego projektu, co wyraża się między innymi mnogością jego
komponentów, bogatym zestawem używanych narzędzi i metod oraz licznością personelu
zaangażowanego w jego realizację.
W miarę jak projektanci programiści wprowadzani są w arkana dziedziny aplikacyjnej
systemu przez przyszłych jego użytkowników, modyfikują sukcesywnie zestaw wymagań zwią-
zanych z tym systemem. Gdy stają się odbiorcami nowych technologii lub doświadczają
rozmaitych ograniczeń ze strony technologii już używanych, modyfikują sam projekt systemu
1 metody jego implementacji. Gdy z kolei użytkownicy żądają nowych cech funkcjonalnych,
a kontrola jakości wynajduje wciąż nowe błędy, zmienia się sam system i towarzyszące mu
produkty pomocnicze. W tym kontekście proces tworzenia aplikacji jawi się wyraźnie jako pa-
smo nieustających zmian.
Owe zmiany, w połączeniu z olbrzymim stopniem komplikacji przedsięwzięcia, wyklu-
czają a priori możliwość skutecznego stawienia im czoła przez pojedynczego człowieka — ten
absolutnie nie jest w stanie zapanować zarówno nad status quo projektu, jak i nad jego nie-
uchronną ewolucją, nawet jeśli kierunek tej ewolucji jest znany i dobrze zdefiniowany. Zbyt
wiele błędów popełnionych na etapie interpretacji dziedziny aplikacyjnej sprawia, że system
staje się nieprzydatny dla użytkowników, a w konsekwencji dyskwalifikuje go pod względem
Wstęp 21

marketingowym. Niedojrzałe lub niekompatybilne technologie implementacyjne wydłużają


proces tworzenia systemu i odbijają się negatywnie na jego niezawodności. Niezdolność do
skutecznego zapanowania nad niezbędnymi zmianami wprowadza do systemu kolejne usterki
i zwykle objawia się degradacją jego wydajności oraz użyteczności.
Książka ta stanowi owoc doświadczeń ponad dziesięcioletniej praktyki w tworzeniu
systemów komputerowych oraz nauczania tej trudnej sztuki na rozmaitych kursach inżynierii
oprogramowania. Przyglądając się baczniej wielu takim kursom, skonstatowaliśmy, że uczą
one postrzegania rzemiosła programistycznego raczej w oderwaniu od rzeczywistego świata,
w kontekście niewielkich, dobrze zdefiniowanych problemów. W rezultacie studenci nabywają
umiejętności znakomitego radzenia sobie z takowymi, lecz pierwsze zderzenie z realiami pro-
gramistycznymi rzeczywistego świata okazuje się dla nich wręcz szokujące — stają w obliczu
niełatwych wyborów pomiędzy rozmaitymi narzędziami i technologiami, a także muszą stawić
czoła niebanalnemu wyzwaniu, którym jest produktywna współpraca w ramach zespołu. Ta
niekorzystna sytuacja zdaje się jednak zmieniać — w programach studiów informatycznych
opisane rozdrobnienie na izolowane, odrębne zadania powoli ustępuje tendencji realizowania
pojedynczego, niebanalnego projektu jako zadania semestralnego.

Narzędzia: UML, Java i wzorce projektowe

Co prawda, pomyśleliśmy tę książkę jako podręcznik akademicki, lecz równie dobrze


sprawdzi się ona w kontekście nieco bardziej doraźnym, jako literatura pomocnicza dla krót-
koterminowych warsztatów czy też projektów badawczo-rozwojowych. Prezentowane przy-
kłady, inspirowane rzeczywistymi projektami z realnego świata, związane są z uznanymi i szeroko
wykorzystywanymi technikami w rodzaju UML (Universal Modeling Language — „uniwer-
salny język modelowania" — technologiami bazującymi na języku Java, wzorcach projektowych,
racjonalizacji projektów, zarządzaniu konfiguracją czy kontroli jakości. Technologie te rodzą
określone problemy w zakresie zarządzania projektami, odbijające się bezpośrednio zarówno
na ich złożoności, jak i na dynamice zmian, którym podlegają.

Zasady

Wykładając szeroko rozumianą inżynierię oprogramowania, kierujemy się pięcioma


wymienionymi poniżej przesłankami.

Praktyka. Wierzymy, że wiedza teoretyczna musi być połączona z doświadczeniami praktycz-


nymi. Studenci mogą zrozumieć pojęcie złożoności nie inaczej, jak tylko pracując nad zło-
żonymi systemami — złożonymi, czyli przekraczającymi możliwości kompletnego zrozumie-
nia przez jedną osobę.

Rozwiązywanie problemów. Jesteśmy przekonani, że edukacja informatyczna musi opierać


się na rozwiązywaniu problemów. Nie istnieją rozwiązania „dobre" ani „złe", a jedynie „lepsze"
lub „gorsze" w świetle przyjętych kryteriów. Doceniając należycie rolę istniejących rozwiązań
znanych problemów i zachęcając do ich wykorzystywania, zwracamy jednocześnie uwagę na
znaczenie krytycyzmu i tendencji do ulepszania zastanego porządku rzeczy.
22 Wstęp

Ograniczoność zasobów. Gdy dysponuje się wystarczającą ilością czasu i zasobów, można
tworzyć systemy niemal idealne. Taka sytuacja jest jednak utopią i to z dwóch zasadniczych
powodów. Po pierwsze, czas realizacji projektu zawsze jest ograniczony, a przeznaczone na tę
realizację zasoby są mniej lub bardziej limitowane. Po drugie — nawet przy założeniu do-
statecznego bogactwa zasobów, gdy szczegóły problemu zmieniają się gwałtownie podczas jego
rozwiązywania, może się zdarzyć, że zbudowany właśnie system rozwiązuje nie ten problem, co
potrzeba. Konieczne jest więc założenie, że proces tworzenia systemu podlega ograniczeniom
w kontekście dostępnych zasobów; założenie takie ma dodatkowo tę cenną zaletę, że dążymy
do jak najlepszego ich wykorzystywania, stosując podejście oparte na komponentach oraz wy-
korzystując wielokrotnie wiedzę, elementy projektu i fragmenty kodu. Innymi słowy — trak-
tujemy tworzenie oprogramowania jako dyscyplinę inżynierską.

Interdyscyplinarność. Tworzenie oprogramowania jest działalnością interdyscyplinarną,


wymagającą olbrzymiej wiedzy z zakresu elektroniki i architektury komputerów, informatyki,
administrowania biznesem, projektowania grafiki, standardów przemysłowych — a często
także umiejętności teatralnych i literackich! Jednocześnie budowanie oprogramowania jest
dziedziną ściśle ukierunkowaną: próbując zrozumieć i modelując dziedzinę aplikacyjną pro-
blemu, programiści zmuszeni są do ciągłej interakcji z innymi programistami, użytkownikami
i klientami, którzy o tajnikach tworzenia oprogramowania mają pojęcie raczej znikome.
Wymaga to umiejętności spojrzenia na tworzony system z wielu różnych perspektyw.

Komunikacja. Nawet wówczas, gdy programiści tworzą oprogramowanie na użytek własny


lub innych programistów, i tak muszą się sprawnie komunikować między sobą. Jako programiści,
nie możemy pozwolić sobie na luksus ograniczania komunikacji interpersonalnej do zwy-
czajowego „dzień dobry" dla kolegi z sąsiedniego boksu: sprawne komunikowanie się to pro-
ponowanie alternatywnych rozwiązań, negocjowanie kompromisów oraz krytyczne i kon-
struktywne spojrzenie na cudzą pracę. Nadspodziewanie wiele przypadków niepowodzenia
w realizowaniu projektów programistycznych ma swe przyczyny w komunikowaniu nie-
właściwej lub nieprawdziwej informacji bądź braku pożądanej informacji w ogóle. Musimy
więc nauczyć się sprawnego porozumiewania się z innymi — nie tylko kolegami programistami,
lecz (co ważniejsze) z klientami i użytkownikami naszych systemów.
Pięć wymienionych przesłanek stanowi osnowę treści tej książki. W naszym zamierzeniu
ma to zachęcić czytelnika do postrzegania złożonych i zmieniających się problemów w kate-
goriach praktycznych środków, dostępnych dla ich rozwiązywania.

Książka

Książka ta prezentuje techniki zorientowane obiektowo w zastosowaniu do inżynierii


oprogramowania. Nie jest ani podręcznikiem programowania, ani też przewodnikiem po
algorytmach i strukturach danych. Skoncentrowaliśmy się raczej na ograniczonym podzbiorze
technik, których zastosowanie prezentujemy w odniesieniu do przedsięwzięcia o wyraźnym
stopniu komplikacji, którego przykładem może być projekt realizowany przez zespół 20 - 60
osobowy. Ów subiektywny wybór ma swe konsekwencje w subiektywizmie ujęcia opisu
— wskazujemy rozwiązania godne, naszym zdaniem, polecenia, nie stroniąc od opisywania
Wstęp

innych, których słabe i mocne strony staramy się wyraźnie eksponować. Mamy zatem nadzieję,
że każdy czytelnik tej książki znajdzie w niej dla siebie coś pożytecznego.
Treść książki, w naszym zamierzeniu przeznaczona jako materiał dla jednosemestralnego
kursu, składa się z 16 rozdziałów, podzielonych na trzy części.
I tak w części pierwszej, zatytułowanej „Wprowadzenie" i obejmującej trzy rozdziały,
zajmujemy się problematyką podstawowych umiejętności programisty, warunkujących jego
zdolność do funkcjonowania w kontekście inżynierii oprogramowania.

• W rozdziale 1., „Wprowadzenie do inżynierii oprogramowania", wyjaśniamy różnicę


między programowaniem a inżynierią oprogramowania i wyzwania, jakie stawia przed
programistami ta ostatnia, definiujemy też podstawowe koncepcje przewijające się
przez treść wszystkich rozdziałów.
• Rozdział 2., „Modelowanie w języku UML", zawiera opis podstawowych elementów
UML (Universal Modeling Language), popularnego języka opisu modelowania, wy-
korzystującego techniki obiektowe. Prezentujemy w nim modelowanie jako jeden
ze sposobów radzenia sobie ze złożonością problemów, pokazujemy, jak czytać i ro-
zumieć diagramy UML (budowaniem takich diagramów, odzwierciedlających różno-
rodne aspekty systemu, zajmujemy się w następnych rozdziałach). Język UML
przewija się przez całość tej książki jako środek opisu rozmaitych artefaktów — od
systemów do procesów i produktów.
• W rozdziale 3., „Organizacja projektu i komunikacja", wprowadzamy podstawowe
koncepcje związane z organizacją projektu i sprawnym komunikowaniem się jego
uczestników. Programiści i menedżerowie spędzają ponad połowę swego zawodowego
czasu na komunikowaniu się — osobiście lub za pośrednictwem e-maili, grup dysku-
syjnych, wideokonferencji czy pisemnej dokumentacji. Podczas gdy modelowanie jest
środkiem radzenia sobie ze złożonością, komunikacja umożliwia sprostanie wyma-
ganiom wynikającym z dynamicznych zmian w projekcie. Opisujemy organizację
projektów i dyskutujemy czynniki warunkujące sprawne komunikowanie się.

W części drugiej — nazwanej „Zmagania ze złożonością" — koncentrujemy się na meto-


dach i technologiach umożliwiających programistom formułowanie specyfikacji, projektowanie
i implementowanie skomplikowanych systemów.

• W rozdziałach 4., „Zbieranie wymagań", i 5., „Analiza wymagań", przedstawiamy


definicję systemu widzianego oczyma jego użytkownika. Na etapie zbierania wymagań
programiści określają niezbędne dla użytkowników elementy funkcjonalne i sposób
ich dostarczania. W trakcie analizy programiści formalizują tę wiedzę, weryfikując
jednocześnie jej kompletność i spójność. Pokażemy tu, w jaki sposób język UML może
okazać się pomocny w procesie ogarniania złożoności dziedziny aplikacyjnej problemu.
• W rozdziałach 6., „Projektowanie systemu — dekompozycja na podsystemy",
i 7., „Projekt systemu: realizacja celów projektowych", definiujemy system z per-
spektywy programisty. Na tym etapie programiści definiują architekturę systemu
w kategoriach celów projektowych i w podziale na podsystemy. Rozwiązywane są
podstawowe programy o charakterze globalnym, takie jak odwzorowanie systemu
w komponenty sprzętowe, sposoby trwałego przechowywania danych czy ogólna
24 Wstęp

kontrola przepływu sterowania. Skupiamy uwagę czytelników na tym, jak programiści


mogą wykorzystywać swoiste style architektoniczne oprogramowania oraz jego kom-
ponenty i jak stosować mogą język UML do radzenia sobie ze złożonością dziedziny
aplikacyjnej.
• W rozdziałach 8., „Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań
wzorcowych", 9., „Projektowanie obiektów: specyfikowanie interfejsów", i 10., „Od-
wzorowywanie modelu na kod", opisujemy szczegółowo związane z modelowaniem
czynności odnoszące się do „dziedziny realizacyjnej", czyli ujęcia systemu w katego-
riach programistycznych. Na tym etapie programiści identyfikują i adaptują wzorce
projektowe oraz środowiska programistyczne niezbędne do realizacji poszczegól-
nych podsystemów. Następuje wówczas precyzyjne określenie interfejsów i klas,
z wykorzystaniem obiektowych języków definiowania ograniczeń, do jakich należy
między innymi OCL (Object Constraint Language) będący częścią UML. W następnej
fazie szczegółowy projekt obiektowy odwzorowywany jest w kod źródłowy i sche-
maty baz danych.
• Przedmiotem rozważań rozdziału 11., „Testowanie", jest weryfikacja zachowania
systemu pod kątem zgodności z modelami. Zadaniem testowania jest wykrywanie
usterek systemu, między innymi wprowadzanych do niego w związku ze zmianami
lub zbieraniem wymagań. Opisujemy tu kilka technik testowania, takich jak testy
biało- i czarnoskrzynkowe, testowanie ścieżek, testowanie oparte na informacji o sta-
nie, inspekcję zmiennych; dyskutujemy ich zastosowanie do systemów zorientowa-
nych obiektowo.

Część trzecia — „Zarządzanie zmianami" — dedykowana jest metodom i technologiom


wspierającym kontrolowanie, ocenę i implementowanie zmian w trakcie całego cyklu rozwo-
jowego systemu.

• W rozdziale 12., „Zarządzanie racjonalizacją", opisujemy proces rodzenia się decyzji


projektowych, ich uzasadniania i moderowania. Modele opracowywanie na etapach
zbierania wymagań, analizy i projektowania stanowią silne narzędzie do stawienia
czoła złożoności zagadnienia, umożliwiają bowiem spojrzenie z różnych perspektyw
na to, co tak naprawdę powinien robić tworzony system i jak powinien to robić. De-
cyzje projektowe, wraz ze swymi alternatywami, oraz uzasadnienie jednych i drugich
składają się na coś, co powszechnie nazywa się racjonalizacją systemu.
• Treść rozdziału 13., „Zarządzanie konfiguracją", to opis technik modelowania historii
projektu. Zarządzanie konfiguracją dopełnia racjonalizację w dziele radzenia sobie
ze zmianami, których doświadcza wciąż przeobrażający się projekt systemu. Zarzą-
dzanie wersjami obejmuje chronologiczną dokumentację ewolucji systemu. Zarządza-
nie edycjami zapewnia spójność i odpowiednią jakość komponentów poszczególnych
edycji. Wreszcie — zarządzanie zmianami daje gwarancję, że kolejne modyfikacje
systemu pozostaną w spójności z jego celami projektowymi.
• Rozdział 14., „Zarządzanie projektem", poświęcony jest technikom inicjowania
projektu programistycznego, śledzenia postępu jego realizacji, a także czynnikowi
ryzyka i nieprzewidywalnym zdarzeniom. Koncentrujemy się tu na organizacji,
Wstęp 25

rolach i działaniach kierowniczych pozwalających na efektywną współpracę dużej


liczby uczestników projektu, zmierzającą do dostarczenia wysokiej jakości produktu
w sposób zgodny z przyjętymi ograniczeniami.
• W rozdziale 15., „Cykl życiowy oprogramowania", opisujemy koncepcję cyklu ży-
ciowego oprogramowania, na przykładzie modelu spiralnego Boehma i modelu jed-
nolitego procesu wytwarzania oprogramowania — oba dostarczają abstrakcje nie-
zbędne do modelowania działań projektantów i programistów. Opisujemy także model
dojrzałości organizacyjnej, wykorzystywany do oceny stopnia zorganizowania całego
procesu wytwarzania oprogramowania.
• Ostatni, 16. rozdział, „Wszystko razem, czyli metodologie", to swoiste podsumowanie,
poświęcone metodologiom i heurystykom związanym z treścią rozdziałów poprzed-
nich i użytecznym w konkretnych sytuacjach. Realizacja każdego projektu o reali-
stycznej skali, niezależnie od tego, jak solidnego w fazie zbierania wymagań i jak
szczegółowo zaprojektowanego, nieuchronnie wiąże się z nieprzewidywalnymi zda-
rzeniami i koniecznością wprowadzania wynikających stąd niezaplanowanych wcze-
śniej zmian. Właśnie zmaganie się z czynnikiem niepewności jest tym aspektem
inżynierii oprogramowania, który wyraźnie odróżnia rzeczywiste projekty i systemy
od opisywanych w podręcznikach. Omawiamy kilka różnych metodologii, zwracamy
uwagę na pewne problemy wspólne dla większości typowych projektów programi-
stycznych, a na zakończenie prezentujemy trzy różne studia przypadków oparte
(co zrozumiałe) na projektach faktycznie realizowanych.

Wymienione powyżej tematy są ze sobą wzajemnie i ściśle powiązane. By to powiązanie


wyeksponować, zastosowaliśmy podejście iteracyjne, którego wyrazem jest podział każdego
rozdziału na pięć sekcji. W pierwszej z nich wprowadzamy czytelnika w podstawowe problemy,
składające się tematycznie na całość rozdziału; wprowadzenie to jest często ilustrowane przy-
kładami zaczerpniętymi z rzeczywistego świata. W sekcji drugiej krótko opisujemy podstawowe
czynności i działania, związane z realizacją konkretnego tematu. Trzecia sekcja zawiera wyja-
śnienie podstawowych koncepcji, zaś w czwartej analizujemy ich detale techniczne, na przykła-
dzie rzeczywistych systemów. Zamykająca rozdział sekcja piąta poświęcona jest aktywnościom
menedżerskim, a także nieuchronnym kompromisom, jakie przychodzi rozstrzygać w związku
z określonym aspektem realizacji projektu.
Przez treść rozdziałów od 4. do 10. przewija się realne studium przypadku — złożony,
wielodostępny system gry zespołowej ARENA. Powracając nieustannie do tych samych kon-
cepcji, ilustrowanych coraz to bardziej złożonymi przykładami, mamy nadzieję dostarczyć
użytkownikom sporo praktycznej wiedzy na temat obiektowo zorientowanej inżynierii opro-
gramowania.

Kurs

Tworzenie ogromnego, złożonego systemu można porównać do wspinaczki na wysoki


szczyt. Warto wówczas znać trasę do celu, tej jednak nie sposób nigdy dokładnie określić a priori,
nie sposób bowiem przewidzieć wszystkich szczelin, które wciąż mogą się niespodziewanie
26 Wstęp

otwierać. Na podobnej zasadzie — mimo iż w tej książce odzwierciedliliśmy rzetelnie naszą


wiedzę z zakresu inżynierii oprogramowania — świat idzie naprzód i metody, którym dziś hoł-
dujemy, mogą się wkrótce okazać przestarzałe.
Jak nauczyć studentów radzenia sobie z błyskawicznie zmieniającymi się uwarunkowa-
niami? Naszym zdaniem (jeśli trzymać się wcześniejszej analogii), nie wystarczy do tego zna-
jomość mapy, potrzebna jest jeszcze umiejętność pokonywania przeszkód w terenie. I jakkolwiek
znajomość trasy jest niezbędna, nie jest w stanie zastąpić wspinaczkowego doświadczenia.
Napisaliśmy tę książkę z przeznaczeniem dla jednosemestralnego kursu z zakresu inży-
nierii oprogramowania, zarówno dla studiów magisterskich, jak i podyplomowych. Zakładamy,
że czytelnicy posiadają doświadczenie w programowaniu w języku wysokiego poziomu, takim
jak C, C++, C# lub Java. Zakładamy także elementarne umiejętności w zakresie rozwiązywania
problemów technicznych, lecz nie oczekujemy doświadczenia w konfrontowaniu tychże z sy-
tuacjami typowymi dla tworzenia złożonych systemów. Oczywiście, książka ta nadaje się równie
dobrze na potrzeby doraźnych kursów, odbywających się poza kontekstem regularnych studiów.

Kursy projektowania i kursy dla zaawansowanych. Kurs projektowania powinien obejmować


wszystkie rozdziały, zasadniczo w ich naturalnej kolejności. W przypadku kursów o stopniu
bardziej zaawansowanym instruktor może wcześniej zająć się tematyką rozdziału 14., „Zarzą-
dzanie projektem", by zawczasu zaznajomić słuchaczy z problematyką kontroli i planowania.

Kursy wprowadzające. Kurs wprowadzający, nierozłącznie związany z zadaniami domowymi,


powinien ograniczać się do trzech pierwszych sekcji każdego rozdziału. Dwie pozostałe sekcje
stanowią doskonały materiał na zadania domowe, w ramach których słuchacze doskonalić
będą swe umiejętności w zakresie tworzenia „na papierze" minisystemów w postaci diagra-
mów UML, dokumentacji i kodu.

Krótkie kursy technologiczne. Książka przydatna będzie także dla krótkich, intensywnych
kursów adresowanych do profesjonalistów. Kursy tego rodzaju, koncentrujące się na języku
UML i metodach obiektowo zorientowanych, powinny czerpać z rozdziałów 1., 2., 4., 5.,
6., 7., 8., 9., 10. i 11. (w tej kolejności), obejmujących wszystkie fazy tworzenia systemu, od
zbierania wymagań do testowania. W kursach bardziej zaawansowanych można także wyko-
rzystać rozdziały 12., „Zarządzanie racjonalizacją", i 13., „Zarządzanie konfiguracją".

Krótkie kursy dla menedżerów. Książka okaże się również pożyteczna na intensywnych
kursach dla menedżerów. Szczególnie przydatne będą rozdziały związane z menedżerskimi
aspektami realizacji projektów — komunikacją, zarządzaniem ryzykiem, racjonalizacją, mo-
delami dojrzałości i językiem UML — czyli rozdziały (kolejno) 1., 2., 3., 14., 15., 16., 12. i 13.

Zmiany w stosunku do wydania drugiego

Redagowanie tego wydania rozpoczęto od przystosowania treści do wersji UML 2 oraz


ostatnich osiągnięć z zakresu „zwinnych" metod projektowania. Dodaliśmy także nieco nowego
materiału związanego z projektowaniem systemów i ich testowaniem. W związku z tym, dzię-
kujemy za cierpliwość naszej redaktor — Tracy Dunkelberger. Oto szczegółowa lista zmian.
Wstęp 27

• Wszechstronne przystosowanie treści do najnowszych standardów UML i OCL. Zrewi-


dowaliśmy pod tym kątem większość diagramów, w szczególności wprowadziliśmy
diagramy komponentowe z notacją „kółko-gniazdo" (ball-and-socket).
• Rozszerzenie o zwinne metody projektowania. W wydaniu drugim, w rozdziale 16.
znajduje się ogólne omówienie technologii eXtreme Programming. To wydanie roz-
szerzyliśmy o opis „zwinnych" metod Serum i rubgy i ich konsekwencji pod wzglę-
dem testowania, zarządzania konfiguracją i zarządzania projektami. Czytelnik znajdzie
je w rozdziałach 11., 13. i 14.
• Nowości na temat ciągłego integrowania. Jedną ze wspomnianych „zwinnych" tech-
nik jest ciągłe integrowanie zmian dokonywanych w oprogramowaniu z jego „głów-
nym pniem" {trunk). I jakkolwiek technika ta pozwala na wczesne identyfikowanie
i usuwanie problemów ogólnie związanych z integracją zmian, to jej realizacja może
w stadium początkowym wiązać się z wieloma wyzwaniami. Piszemy o tym w roz-
dziale 13., „Zarządzanie konfiguracją".
• Nowości na temat specyfikacji U2TP i zautomatyzowanego testowania. Na przykładzie
rozszerzeń UML 2 Testing Profile dyskutujemy pewne koncepcje testowania, między
innymi różnicę między systemem testującym a systemem podlegającym testowaniu.
U2TP posłużyło nam także do wzbogacenia opisu o automatyzację testowania i au-
tomatyczne generowanie przypadków testowych.
• Ulepszenia w zakresie studiów przypadku i lepszy dobór przykładów. Od czytelników
poprzedniego wydania otrzymaliśmy w tym względzie wiele uwag, za które bardzo
dziękujemy — zaimplementowaliśmy większość z nich, nie sposób jednak wymienić
ich tutaj szczegółowo.

Konwencje typograficzne

Aby treść książki była bardziej czytelna, zróżnicowaliśmy styl jej poszczególnych ele-
mentów, i tak:

• gdy nowy termin pojawia się po raz pierwszy, wyróżniamy go czcionką pogrubioną,
• tytuły książek i rozdziałów, wyróżnione terminy oraz adresy URL pisane są kursywą,
• nazwy systemów i nazwy związane z modelowanymi elementami (klasami, atrybu-
tami, operacjami, stanami, zmiennymi) pojawiają się w postaci czcionki o s t a ł e j
szerokości,
• nazwy klas abstrakcyjnych pisane są pochylę czcionką o stałej szerokości,
• nazwy obiektów na rysunkach mają postać podkreślonej czcionki o s t a ł e j
szerokości,
• przykładowy kod źródłowy pisany jest czcionką o s t a ł e j szerokości, przy czym
słowa kluczowe są pogrubione, a komentarze pochylone.
28 Wstęp

Notka produkcyjna

Treść książki została napisana i złożona za pomocą programu Adobe Framemaker.


Ostatecznie przyjęła formę plików PDF sporządzonych przy użyciu programu Adobe Acrobat
Distiller.

Autorzy

Dr Bernd Bruegge studiował inżynierię oprogramowania, a później nauczał jej przez


dwadzieścia lat na Uniwersytecie Carnegie Mellon, gdzie uzyskał najpierw dyplom magistra,
a potem doktoryzował się. Uzyskał także dyplom uniwersytetu w Hamburgu. Jest obecnie
profesorem informatyki na Uniwersytecie Technicznym w Monachium, gdzie zajmuje się
zastosowaniami inżynierii oprogramowania, współpracuje także jako adiunkt na Uniwer-
sytecie Carnegie Mellon. W tej książce zebrał owoc piętnastoletnich doświadczeń z zakresu
nauczania obiektowo zorientowanej inżynierii oprogramowania — materiał dostępny w licz-
nych publikacjach drukowanych i na stronach WWW. W 1995 roku Uniwersytet Carnegie
Mellon przyznał mu nagrodę Herbert A. Simon Excellence in Teaching Award. Jest także kon-
sultantem o zasięgu międzynarodowym. Opisywane w tej książce techniki wykorzystywał do
implementowania wielu złożonych systemów, między innymi systemu zarządzania projektami
dla firmy Daimler Chrysler, systemu modelowania środowiska naturalnego na zamówienie
U.S. Environmental Protection Agency oraz systemu zarządzania roszczeniami powypadko-
wymi na potrzeby lokalnego departamentu policji — że ograniczymy się tylko do kilku
naj ważniej szych.
Dr Allen Dutoit pracuje w przemyśle lotniczym jako specjalista tworzenia oprogramo-
wania poświęconego awionice. Ukończył studia magisterskie, a później doktoryzował się na
Uniwersytecie Carnegie Mellon, uzyskał także dyplom inżyniera Szwajcarskiego Federalnego
Instytutu Technologicznego w Lozannie. Od 1993 roku współpracuje z prof. Bruegge zarów-
no na Uniwersytecie Carnegie Mellon, jak i na Uniwersytecie Technicznym w Monachium,
gdzie wykorzystuje i doskonali metody opisywane w tej książce. Jego prace badawcze obejmują
wiele obszarów inżynierii oprogramowania i systemów zorientowanych obiektowo, między
innymi inżynierię zbierania wymagań i zarządzania nimi, zarządzanie racjonalizacją, tworze-
nie oprogramowania w warunkach rozproszenia oraz systemy bazujące na prototypach.
Uprzednio współpracował z instytutem złożonych systemów inżynierskich Uniwersytetu
Carnegie Mellon.

Fotografie

Zdjęcia widoczne na początku każdego z rozdziałów zostały zrobione podczas wejścia


na Denali (6193 m) w stylu alpejskim drogą przez West Rib, dokonanego przez jednego z auto-
rów tej książki, zanim jeszcze rozpoczęły się prace związane z jej powstaniem. Podczas tej
ekspedycji analogie między alpinizmem a tworzeniem oprogramowania stały się więcej niż
oczywiste. Czytelnicy zobaczyć mogą mała kronikę wyprawy: nasz ekspedycyjny wehikuł
na autostradzie łączącej Alaskę z Kanadą (początek części I), Mount Robson, najwyższą
górę kanadyjskich Gór Skalistych — 3954 m — ze ścianą Kaina (Kain Face) (rozdział 1.),
Wstęp 29

widok z samolotu na Denali (rozdziały 2. i 4.), początek drogi przez West Rib (rozdział 3.),
widok z West Rib 1000 m w dół (zauważalne są nasze ślady na East Kahiltna Glacier (roz-
dział 5.), Mount Foraker z obozu 5. (rozdział 6.), piękną, lecz trudną do zdobycia grań na wy-
sokości 5000 m (rozdział 7.), obóz bazowy na drodze normalnej, gdzie wykorzystaliśmy resztki
igloo (rozdział 8.), lądowisko dla samolotu Douga Geetinga (rozdział 9.), biwak na drodze przez
West Rib, zwany „Hotel Crux", bo nie da się tam wykopać platformy wystarczającej do rozbicia
namiotu (rozdział 10.), przekraczanie szczeliny brzeżnej(rozdział 11.), świeże lawinisko (roz-
dział 12.), Denali z drogą Cassina (rozdział 13.), plany różnych dróg na szczyt (rozdział 14.),
„horyzontalny" wschód słońca u podnóża drogi Cassina (rozdział 15.) i wierzchołek Denali.
A na fotografii z okładki majestatycznie prezentuje się szczyt K2.
Podziękowania

I ^ s i ą ż k a ta w trakcie swego cyklu rozwojowego doznała szeregu złożonych zmian. W 1989


roku jeden z autorów (Bernd Bruegge) rozpoczął serię kursów, których celem było zaznajo-
mienie studentów z realiami typowych projektów informatycznych, realizowanych dla praw-
dziwych klientów, przy użyciu prawdziwych narzędzi. W pierwszym takim kursie, zareje-
strowanym w katalogu Uniwersytetu Carnegie Mellon pod numerem 15-413, uczestniczyło
dziewiętnastu studentów, a jego owocem był kod o rozmiarze 4000 wierszy. Zainspirowani
książką Jamesa Rumbaugha i jego kolegów, traktującą o obiektowo zorientowanym modelo-
waniu i projektowaniu, poczęliśmy wdrażać w praktyce opisywane w niej metody. Zorganizo-
waliśmy kilka rozproszonych geograficznie kursów, obejmujących kilkudziesięciu studentów
z Uniwersytetu Carnegie Mellon i Uniwersytetu Technicznego w Monachium, czego owocem
była każdorazowo dokumentacja o objętości około 500 stron i kod liczący 50 000 wierszy.
Obecnie realizujemy podobny kurs dla studentów Uniwersytetu w Otago w Nowej Zelandii
i Uniwersytetu Technicznego w Monachium.
Podstawowym mankamentem wielu kursów z zakresu inżynierii oprogramowania jest
niezdolność instruktorów do dystansowania się od problemów, jakie napotykają studenci;
w rezultacie instruktorzy ci mimowolnie stają się uczestnikami projektów, najczęściej w roli ich
menedżerów. Mamy nadzieję, że ta książka okaże się pomocna w dziele przywracania właści-
wych proporcji w tym względzie.
Niezależnie od dużego wysiłku, jaki włożyliśmy w realizację wspomnianych kursów,
znaleźliśmy wystarczająco dużo czasu na napisanie tej książki i opracowanie kolejnych jej wy-
dań, co zawdzięczamy głównie pomocy i cierpliwości wielu studentów, klientów, asystentów,
pracowników technicznych, korektorów i innych współpracowników, załodze Prentice Hall
i — przede wszystkim — naszym rodzinom. Wielu z nich przyczyniło się bezpośrednio do
usprawnienia i uatrakcyjnienia kursów, inni dostarczyli nam cenne uwagi na temat szkiców
i projektów, inni jeszcze okazywali znaczącą pomoc, gdy sprawy zaczynały się komplikować
i właśnie w tym miejscu chcielibyśmy wyrazić wdzięczność, jaką z perspektywy dwudziestu lat
całej historii winni jesteśmy wielu ludziom, których postaramy się tu wymienić.

Uczestnicy kursów projektowych: Workstation Fax (1989), Interactive Maps (1991), Interac-
tive Pittsburgh (1991), FRIEND (1992, 1993, 1994), JEWEL, GEMS (1991, 1994, 1995),
DIAMOND (1995, 1996), OWL (1996, 1997), JAMES (1997, 1998), PAID (1998, 1999),
STARS (1999, 2000, 2001), TRAMP (2001, 2002), ARENA (2002, 2003), CampusTV
(2004,2005), Virtual Symphony Orchester (2005), WALOS (2006) i DOLLI (2007, 2008).
32 Podziękowania

Pomocnicy i asystenci, którzy okazali nam wiele serdeczności i nieocenioną pomoc


w trudnych sytuacjach: Martin Bauer, Ulrich Bauer, Catherine Copetas, Oliver Creighton,
Ava Cruse, Barry Eisel, Luca Girardo, Dieter Hege, Mariss Jansons, Joyce Johnstone, Siegfried
Kiese, Siegfried Klinkhammer, Rafael Kobyliński, Marc Lindike, Asa MacWilliams, Monika
Marki, Key Maerkl i jego kwartet Aritus, Pat Miller, Martin Ott, Ralf Pfleghar, Martin Pittenauer,
Harald Ranner, Joachim Reichel, Max Reiss, Barbara Sandling, Christian Sandor, Ralph Schiessl,
Arno Schmackpfeffer, Helma Schneider, Stephan Schoenig, Stefien Schwarz, Martin Wagner,
Uta Weber, Timo Wolf i Michael Zaddach.

Pomocnicy, asystenci i przyjaciele, dostarczający nam wciąż nowych pomysłów i inspiracji:


Mario Barbacci, Len Bass, Ben Bennington, Elizabeth Bigelow, Roberto Bisiani, Naoufel
Boulila, Harry Q Bovik, Andreas Braun, Manfred Broy, Sharon Burks, Marvin Carr, Mike
Collins, Robert Coyne, Douglas Cunningham, Michael Ehrenberger, Kim Faught, Peter
Feiler, Allen Fisher, Laura Forsyth, Eric Gardner, Helen Granger, Thomas Gross, Volker
Hartkopf, Bruce Horn, David Kauffer, Gudrun Klinker, Kalyka Konda, Suresh Konda, Rich
Korf, Birgitte Krogh, Sean Levy, Frank Mang, K C. Marshall, Dick Martin („Tang Soo"), Horst
Mauersberg, Roy Maxion, Russ Milliken, Ira Monarch, Rhonda Moyer, Robert Patrick,
Brigitte Pihulak, Mark Pollard, Martin Purvis, Raj Reddy, Yoram Reich, James Rumbaugh,
Johann Schlichter, Mary Shaw, Jane Siegel, Daniel Siewiorek, Asim Smailagic, Mark Stehlik,
Eswaran Subrahmanian, Stephanie Szakal, Tara Taylor, Michael Terk, Gunter Teubner, Marc
Thomas, Walter Tichy, Jim Tomayko, Blake Ward, Alex Waibel, Art Westerberg, Jeannette
Wing i Tao Zhang.

Redaktorzy i korektorzy, którzy wskazali nam nasze uchybienia i udzielali cennych wska-
zówek: Martin Barrett, Brian Berenbach, Alex Borgida, Ramsey Bualuan, Dave Chesney,
Andrea De Lucia, Debora East, Thomas Eichhorn, Henry Etlinger, Ray Ford, Jim Helm, Jonas
Helming, Korbinian Herrmann, Allen Holliday, John Keklak, Robert Lechner, Max Koegel,
Jonathan Maletic, Jeff McKinstry, Bruce Maxim, Gerhard Mueller, Michael Nagel, Helmut
Naughton, Barbara Paech, Dennis Pagano, Daniel Paulish, Joan Peckham, Gary Pollice, David
Rine, Florian Schneider, Ingo Schneider, Anthony Sullivan, Damla Turgut i wielu nieznanych
nam z imienia i nazwiska. Wszelkie błędy, które mimo to uchowały się w kolejnych wydaniach,
zawinione są wyłącznie przez nas.

Ekipa z Prentice Hall, która pomogła nam przydać realizmu naszym książkom, szczególnie:
Alan Apt — nasz pierwszy wydawca, który nie pozwolił nam stracić wiary we własne siły;
Lakshmi Balasubramanian, Toni Holm, Patrick Lindner, Camille Trentacoste, Jake Warde
i (przy obecnej edycji) Tracy Dunkelberger, Scott Disanno, a także wielu innych, których ciężka
praca umożliwiła doprowadzenie wydania do szczęśliwego końca i którym nie mieliśmy
okazji podziękować osobiście.

I na koniec — naszym rodzinom, którym tę książkę dedykujemy i których nieskończona mi-


łość i cierpliwość pozwoliła nam często dokonywać rzeczy graniczących z niemożliwością.
w
1.1. Wprowadzenie: niepowodzenia w inżynierii oprogramowania 36

1.2. Czym jest inżynieria oprogramowania? 38


1.2.1. Modelowanie 38
1.2.2. Rozwiązywanie problemów 40
1.2.3. Pozyskiwanie wiedzy 41
1.2.4. Racjonalizacja 42

1.3. Podstawowe koncepcje inżynierii oprogramowania 43


1.3.1. Uczestnicy i role 44
1.3.2. Systemy i modele 46
1.3.3. Produkty 46
1.3.4. Aktywności, zadania i zasoby 47
1.3.5. Wymagania funkcyjne i pozafunkcyjne 48
1.3.6. Notacje, metody i metodologie 48

1.4. Aktywności inżynierii oprogramowania 49


1.4.1. Zbieranie wymagań 50
1.4.2. Analiza 51
1.4.3. Projekt systemu 51
1.4.4. Projektowanie obiektów 53
1.4.5. Implementowanie 53
1.4.6. Testowanie 54

1.5. Zarządzanie tworzeniem oprogramowania 54


1.5.1. Komunikacja 55
1.5.2. Zarządzanie racjonalizacją 55
1.5.3. Zarządzanie konfiguracją oprogramowania 56
1.5.4. Zarządzanie projektem 56
1.5.5. Cykl życiowy oprogramowania 56
1.5.6. Podsumowanie 57

1.6. ARENA — analiza przypadku 57

1.7. Literatura uzupełniająca 58

1.8. Ćwiczenia 59

Bibliografia 61
Wprowadzenie
do inżynierii
oprogramowania
Programista amator ma to do siebie, że wciąż poszukuje magicznych
narzędzi, które sztukę tworzenia aplikacji miałyby sprowadzić
do banalnego zadania. W chwili gdy uświadamia sobie, że takich
narzędzi po prostu nie ma, staje się programistą profesjonalistą.
* — Grady Booch Object-Oriented Analysis and Design

O k r e ś l e n i e „inżynieria oprogramowania" narodziło się w roku 1968 jako reakcja na wyob-


cowany status trudnej sztuki tworzenia oprogramowania wysokiej jakości, w założonych ra-
mach czasowych i za cenę nieprzekraczającą założonego budżetu. Przejawem wspomnianego
„wyobcowania" była niezdolność programistów do określenia konkretnych celów, do oszaco-
wania zasobów niezbędnych do osiągnięcia tych celów oraz do sprostania — wciąż zmieniają-
cym się, bo coraz lepiej uświadamianym — oczekiwaniom klientów. I tworzenie typowego
projektu wyglądało mniej więcej tak, że na początku obiecywano Księżyc, potem realizowano
coś na kształt łunochodu, a ostatecznie klienci otrzymywali parę kwadratowych kółek.
W inżynierii oprogramowania istotne są oba człony składowe — inżynieria i opro-
gramowanie. Inżynier to człowiek obdarzony umiejętnościami tworzenia wysokiej jakości
produktów przy wykorzystywaniu standardowych komponentów „z półki" i integrowaniu
ich w ramach ograniczonego czasu i ograniczonego budżetu. Inżynier najczęściej staje w obli-
czu źle zdefiniowanych problemów i jedynie częściowych rozwiązań, a dokonując rozmaitych
wyborów, kierować się musi (z braku lepszego kryterium) rozmaitymi metodami empirycz-
nymi. I jakkolwiek inżynierowie zajmujący się projektowaniem systemów ruchu lotniczego
czy budowaniem mostów radzą sobie z takimi wyzwaniami stosunkowo nieźle, to „inżynie-
rowie od programowania" mają w tym względzie zdecydowanie mniej szczęścia.
Problematyka tworzenia i dostarczania skomplikowanych systemów była i jest inten-
sywnie badana. Przyczyny wspomnianych niepowodzeń upatrywano dosłownie we wszyst-
kim: niezrozumieniu ze strony klientów („co to znaczy, że nie mogę mieć Księżyca za 50 bak-
sów?"), dosłownym znaczeniu frazy „soft" w rzeczowniku „software" („Gdybym tylko mógł
dodać tę jedną, ostatnią cechę...") i wreszcie w niedojrzałości (w sensie młodego wieku) samej
dyscypliny. Gdzież więc leży rzeczywisty problem?

Złożoność i zmiany

Prawdziwie użyteczny system informatyczny jest z konieczności systemem odpowied-


nio skomplikowanym. By mógł on zachować swą użyteczność w obliczu zmieniających się
potrzeb klientów i ewoluującego środowiska docelowego, sam musi nieustannie doznawać
odpowiednich przeobrażeń. Celem tej książki jest zapoznanie czytelnika z metodami, które
36 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

u m o ż l i w i a j ą s k u t e c z n e r a d z e n i e sobie z a r ó w n o ze w s p o m n i a n ą złożonością, jak i z n i e u s t a n n i e


d o k o n u j ą c y m i się z m i a n a m i ; w t y m rozdziale p r z e d s t a w i a m y m o t y w a c j ę d o wykorzystywania
t e c h n i k z o r i e n t o w a n y c h o b i e k t o w o i d e f i n i u j e m y p o d s t a w o w e k o n c e p c j e p r z e w i j a j ą c e się
przez treść tego i następnych rozdziałów.

1.1. Wprowadzenie: niepowodzenia w inżynierii oprogramowania


«

S p ó j r z m y n a p o n i ż s z e p r z y k ł a d y , z a c z e r p n i ę t e z k s i ą ż k i P. N e u m a n n a [ N e u m a n n , 1995].

Błąd roku 1 9 0 0
W 1992 roku Mary z miasteczka Winona w stanie Minnesota otrzymała zaproszenie do dziecięcego
parku zabaw. Nie byłoby w tym nic dziwnego, gdyby nie fakt, że Mary skończyła właśnie 104 lata.
Błąd roku p r z e s t ę p n e g o

Pewien supermarket zapłacił 1000 dolarów grzywny za sprzedaż przeterminowanych porcji mięsa
mielonego. Ich przydatność do spożycia, zgodnie z nadrukiem na etykiecie, kończyła się 1 marca 1988
roku, podczas gdy faktyczny termin ważności upływał dzień wcześniej. W programie drukującym
etykietki przeoczono fakt, że rok 1988 jest rokiem przestępnym.

N a d u ż y c i e interfejsu

10 kwietnia 1990 pociąg londyńskiego metra odjechał ze stacji bez maszynisty. Maszynista nacisnął
starter, ale przecież wiedział, że pociąg pozostanie unieruchomiony, dopóki nie zamkną się wszystkie
drzwi; zablokował w tym celu drzwi najbliższe kabinie i wyszedł z pociągu. Nieświadomy niczego
konduktor chwilę później usunął blokadę ze wspomnianych drzwi — te się zamknęły i . . .

Bezpieczeństwo

CERT (Computer Emergency Response Team)' w Software Engineering Institute jest organizacją
rządu USA, zajmującą się bezpieczeństwem informacji — jego naruszeniem, jego zagrożeniami
i wiedzą dotyczącą jego zapewnienia. Liczba zgłoszonych przypadków naruszenia owego bezpieczeń-
stwa zwiększyła się z 252 w 1990 roku do 21 756 w roku 2000, by w roku 2001 przekroczyć liczbę 40 000.

Za późno i poza budżetem

W 1995 roku błąd w zautomatyzowanej przechowalni bagażu w międzynarodowym porcie lotni-


czym w Denver powodował fizyczne niszczenie walizek. Port otwarto z 16-miesięcznym opóźnie-
niem, po przekroczeniu zakładanego budżetu o 3,2 mld dolarów, z ręcznie sterowanym syste-
m e m przechowywania bagażu.

Z a p ó ź n o i p o z a b u d ż e t e m na bis

W 2002 roku system kontroli ruchu lotniczego w Swanick zawiadywał wszystkimi trasami lot-
niczymi przebiegającymi nad Anglią i Walią. Przy jego realizacji znacznie przekroczono budżet
— zamiast planowanych 350 min funtów wydano ostatecznie 623 min, a realizacje tę ukończo-
no z 6-letnim opóźnieniem. Dwie główne poprawki wprowadzano do systemu w czasie, gdy
trwało już szkolenie personelu kontroli lotów.

1
Zespół ds. reagowania na przypadki naruszenia bezpieczeństwa teleinformatycznego — organizacja utwo-
rzona w listopadzie 1988 roku przez DARPA, po znanym incydencie z robakiem Morrisa — przyp. tłum,
cytat z Wikipedii, patrz http://pl.wikipedia.org/wiki/CERT i http://pl.wikipedia.org/wiki/Robak_Morrisa.
1.1. Wprowadzenie: niepowodzenia w inżynierii oprogramowania 37

Planowe ukończenie projektu


W 1984 roku, po półtorarocznej realizacji i wydaniu 200 min dolarów, oddano do użytku w Wisconsin
system ubezpieczeń zdrowotnych. Ukończony terminowo system pracował jednak niepoprawnie:
doprowadzenie go do działania zgodnie z oczekiwaniami trwało 3 następne lata i kosztowało dodat-
kowe 60 min.

Niepotrzebne komplikacje
Stworzenie samolotu C-17 firmy McDonnell Douglas pochłonęło 500 min dolarów ponad plan,
z powodu licznych błędów w systemie pokładowym. Trudno się temu dziwić, zważywszy na fakt, że
wspomniany system składał się z 19 komputerów i 80 mikroprocesorów, oprogramowanych przy
użyciu 6 różnych języków. 4

Każde z opisanych powyżej niepowodzeń daje się w prostej linii wywieść od problemów
związanych z oprogramowaniem. W niektórych przypadkach programiści nie uwzględnili
rzadko zdarzających się sytuacji (ludzie żyjący dłużej niż 100 lat, rok przestępny i okres
przydatności do spożycia przecinający granicę lutego i marca), kiedy indziej ich wyobraźnia
okazała się niewystarczająca do uwzględnienia nietypowych zachowań ludzkich (opuszczenie
kabiny przez maszynistę po uprzednim włączeniu startera, eksploatowanie przez hakerów
luk bezpieczeństwa w oprogramowaniu sieciowym). Innym jeszcze razem dało o sobie znać
nieudolne zarządzanie realizacją systemu, czego efektem było przekroczenie ram czasowych
i budżetowych, bądź też dostarczenie, owszem, na czas i w założonym budżecie, systemu nie-
spełniającego oczekiwań klientów albo systemu nadmiernie skomplikowanego i stąd wysoce
podatnego na awarie.
Systemy informatyczne są tworami skomplikowanymi. Wykonują zwykle wiele funkcji,
tworzone są z myślą o osiągnięciu wielu różnych, często sprzecznych ze sobą, celów. Składają
się z wielu komponentów, z których większość to gotowe produkty innych („trzecich") pro-
ducentów, skomplikowane same w sobie — tworzone przy udziale specjalistów z wielu różnych
dziedzin. Realizacja typowego systemu informatycznego i jego cykl życiowy rozciągają się za-
zwyczaj na wiele lat. To wszystko przesądza o tym, że prawdziwy system informatyczny przekra-
cza możliwości ogarnięcia wszystkich jego szczegółów przez pojedynczą osobę. Niektóre systemy
są na tyle trudne do zrozumienia już na etapie opracowywania, że ich realizacja nie zostaje
ukończona — oprogramowanie tej fatalnej kategorii zyskało sobie potoczną nazwę vaporware2.
Projekty systemów informatycznych są z natury obiektami podatnymi na nieustanne
zmiany. Wymagania stawiane złożonemu systemowi same są skomplikowane, a często wymagają
też weryfikacji, gdy odkryty zostanie błąd w rozumowaniu bądź też programiści, w miarę
postępu w realizacji systemu, zaczynają coraz lepiej rozumieć rozmaite jego aspekty. Jeśli re-
alizacja takiego projektu ciągnie się przez lata, wiąże się z zaangażowaniem licznego personelu,
wymagającego niezbędnych szkoleń i treningu. Na przestrzeni owych wielu lat zmieniają się
także dostępne technologie — cykl życiowy określonej technologii implementacyjnej jest zwykle
krótszy niż cykl życiowy systemu, na potrzeby realizacji którego można tę technologię wy-
korzystywać. Jeżeli w tych warunkach menedżerowie projektu potraktują wymagania stawiane
systemowi jako byt raz określony i niezmienny, możemy być prawie pewni, że owocem
owych wielu lat pracy będzie system niespełniający oczekiwań użytkowników.

Ang. vapor — opar, mgła, mrzonka, trafna analogia czegoś, co nie miało szczęścia fizycznie zaistnieć,
mimo szczytnych koncepcji i celów — przyp. tłum.
38 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

W następnej sekcji zaprezentujemy wysokopoziomowe spojrzenie na inżynierię opro-


gramowania — opiszemy tę dyscyplinę z perspektywy nauki, inżynierii, pozyskiwania wiedzy
i formalizacji. W sekcji 1.3 opiszemy bardziej szczegółowo poszczególne terminy i koncepcje
obecne w treści tej książki. W sekcji 1.4 dokonamy ogólnego przeglądu programistycznych
aktywności składających się na inżynierię oprogramowania, zaś sekcja 1.5 to ogólny obraz
aktywności na szczeblu zarządzania projektem.
4

1.2. Czym jest inżynieria oprogramowania?


Inżynieria oprogramowania jest w swej istocie modelowaniem. To właśnie za pomocą mo-
delowania programiści radzą sobie ze złożonością, skupiając się w danej chwili na aktualnie
istotnych szczegółach i ignorując wszelkie inne. Realizacja systemu wiąże się z budowaniem
wielu różnych modeli zarówno samego systemu, jak i jego dziedziny aplikacyjnej.
Inżynieria oprogramowania to niewątpliwie rozwiązywanie problemów. Modele służą
do poszukiwania akceptowalnych rozwiązań, a poszukiwanie to prowadzone jest na drodze
eksperymentalnej. Programiści dysponują ograniczonymi zasobami i związani są ogranicze-
niami wynikającymi z dostępnego budżetu oraz harmonogramu. W obliczu (zazwyczaj) braku
fundamentalnych teorii zmuszeni są polegać jedynie na empirycznych metodach dokonywania
wyboru spośród wielu możliwych rozwiązań.
Inżynieria oprogramowania wiąże się z pozyskiwaniem wiedzy. Podczas modelowania
dziedziny aplikacyjnej i dziedziny realizacyjnej programiści kolekcjonują dane, przetwarzając
je najpierw w użyteczną informację, a następnie formalizując tę ostatnią w formie wiedzy.
Pozyskiwanie wiedzy nie jest bynajmniej procesem sekwencyjnym — poznanie nowego, drob-
nego nawet szczegółu może sprawić, że dotychczas budowany model w jednej chwili wali się
w gruzy.
Inżynieria oprogramowania jest procesem sterowanym racjonalizacją. Pozyskując nie-
zbędną wiedzę i podejmując na jej podstawie rozmaite decyzje dotyczące systemu i jego dziedziny
aplikacyjnej, programiści uwzględniać muszą kontekst, w którym decyzje te są podejmowane,
i każdą z nich odpowiednio uzasadniać. Informacja związana z racjonalizacją reprezentowana
w formie odnośnych modeli umożliwia programistom należyte zrozumienie implikacji pro-
ponowanych zmian, gdy przychodzi ponownie rozważyć zasadność podjętej decyzji.
Dalej opiszemy bardziej szczegółowo inżynierię oprogramowania w kategoriach wy-
mienionych aktywności — modelowania, rozwiązywania problemów, pozyskiwania wiedzy
i racjonalizacji. Nieodłącznym aspektem każdej z nich jest czynnik ludzki oraz ograniczenia
wynikające z harmonogramu i dostępnego budżetu. No i nie wolno zapomnieć o zmianach,
które dokonywać się mogą w każdej niemal chwili.

1.2.1. Modelowanie
Zadaniem nauki jest opisywanie i rozumienie złożonych systemów, takich jak atomowa struk-
tura materii, zachowania ludzi i społeczności, czy Układ Słoneczny. Usankcjonowany, zgod-
nie z tradycją, podział dyscyplin naukowych na nauki przyrodnicze i nauki społeczne wyznacza
granicę umożliwiającą odróżnienie dwóch kategorii systemów. Przedmiotem zainteresowania
nauk przyrodniczych — biologii, chemii, fizyki czy paleontologii — jest przyroda (natura)
i jej podsystemy; dla odmiany, przedmiotem zainteresowania psychologii i socjologii, jako
nauk społecznych, jest ludzkie życie oraz interakcje międzyludzkie w ramach społeczności.
1.2. Czym jest inżynieria oprogramowania? 39

Odrębną grupę systemów stanowią systemy sztuczne, których przykładem może być
statek kosmiczny, rozproszona aplikacja rezerwacji miejsc w samolotach czy system obsługu-
jący transakcje giełdowe. H. Simon [Simon, 1970] określił nawet dyscypliny naukowe zajmu-
jące się takimi systemami jako sciences of the artificial („nauki o sztuczności"). Podczas gdy
nauki przyrodnicze i społeczne mają za sobą kilkusetletnią historię, sciences of the artificial
są bytem stosunkowo młodym — na przykład informatyka, rozumiana jako naukowe ujęcie
rozmaitych aspektów systemów komputerowych, jest dzieckiem XX wieku.
Wiele metod, z powodzeniem stosowanych w ramach nauk przyrodniczych i społecz-
nych, przydaje się także w odniesieniu do sciences of the artificial — jedną z nich jest modelo-
wanie. Model stanowi abstrakcyjną reprezentację systemu, umożliwiającą udzielenie odpo-
wiedzi na wiele pytań formułowanych pod adresem tegoż systemu. Model systemu staje się
użyteczny i często niezastąpiony, w sytuacji-gdy oryginalny system byłby zbyt ogromny, zbyt
miniaturowy, zbyt skomplikowany czy zbyt drogi w kontekście eksperymentowania. Modele
okazują się koniecznym substytutem systemów, które już nie istnieją, oraz tych, których ist-
nienie jest dopiero planowane.
Paleontolodzy odnajdują zachowane fragmenty kości i uzębienia dinozaurów, których
z oczywistych względów nigdy nie widzieli w naturze. Z tych fragmentów rekonstruują mo-
dele organizmów zwierzęcych, opierając się na regułach anatomii. Im więcej dostępnego
materiału z wykopalisk, tym klarowniejsza staje się idea, jak poszczególne elementy tego
materiału poskładać ze sobą, i tym większe zaufanie do modelu mającego odzwierciedlać
rzeczywisty organizm żyjącego w zamierzchłych czasach dinozaura. Dysponując dostateczną
ilością kości, zębów i szponów, mogą zyskać niemal całkowitą pewność co do tego, że ich
model odzwierciedla rzeczywistość z należytą dokładnością; co więcej — mogą formułować
rozmaite hipotezy dotyczące jego brakujących elementów: przykładowo, nogi występują zwykle
parami, jeśli zatem dysponują kośćmi jedynie lewej nogi, mogą w sposób niemal pewny
odtworzyć postać brakującej prawej nogi oraz jej pozycję w budowanym modelu. Tak z grubsza
prezentuje się modelowanie systemów, które już nie istnieją.
Podobnie ma się rzecz z modelowaniem systemów nierealistycznych bądź nieogarnio-
nych jak Wszechświat. Fizycy zajmujący się badaniem materii w obliczu wysokich energii
znajdują się na pozycji porównywalnej z położeniem paleontologów odnajdujących szczątki
prehistorycznych organizmów. Obserwując zachowanie cząstek elementarnych i mierząc ich
rozmaite właściwości, próbują stworzyć model materii i energii odzwierciedlający zarówno
ich makrostrukturę, jak i strukturę na poziomie subatomowym. Wieloletnie eksperymenty
przy użyciu akceleratorów cząstek elementarnych dostarczają fizykom coraz to więcej dowo-
dów na to, że budowane przez z nich modele należycie odzwierciedlają rzeczywistość, i jedno-
cześnie pozwalają im żywić nadzieję na to, że brakujące jeszcze ich elementy, gdy zostaną
już zidentyfikowane, również wpiszą się gładko w tzw. model standardowy.
W przypadku obu typów modelowania mamy do czynienia z encjami dwóch kategorii:
systemem rzeczywistym, manifestującym swą obecność poprzez fakty i zjawiska, oraz mode-
lem jego dziedziny aplikacyjnej, stanowiącej zbiór wzajemnie uzależnionych od siebie kon-
cepcji. Do pierwszej z wymienionych kategorii należą dinozaury i cząstki elementarne, do
drugiej — opisy tych ich aspektów, które okazują się istotne (relewantne) dla rozwiązywanego
właśnie problemu.
40 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

Inżynieria oprogramowania jawi się w opisywanym kontekście jako coś pośredniego


między paleontologią i fizyką wysokich energii. Po pierwsze, programiści muszą zrozumieć
środowisko, w którym pracować ma ich system: jeśli jest to system sterowania ruchem ko-
lejowych, muszą dokładnie poznać obowiązujące w tym ruchu reguły sygnalizacji; jeśli jest to
system zarządzania giełdą, mujzą znać zasady obrotu akcjami. Programista nie musi jednak
być certyfikowanym maszynistą ani doświadczonym brokerem: dla niego istotne są jedynie te
koncepcje spośród dziedziny aplikacyjnej, które związane są bezpośrednio z samym systemem
— te bowiem są niezbędne do zbudowania modelu tej dziedziny.
Po drugie, programiści muszą rozumieć systemy, które będą tworzyć, ponieważ tylko
wtedy będą mogli rozstrzygać rozmaite kompromisy i dokonywać trafnych wyborów spośród
dostępnych rozwiązań. Większość systemów przekracza swą złożonością możliwość zrozu-
mienia ich przez jedną osobę, realizacja takich systemów jest ponadto kosztownym przedsię-
wzięciem. By uporać się z jego złożonością i sprostać jego wymogom ekonomicznym, pro-
gramiści opisują istotne aspekty „swojego" systemu, tworząc w ten sposób model dziedziny
realizacyjnej.
Istotą podejścia zorientowanego obiektowo jest połączenie modeli obu dziedzin — apli-
kacyjnej i realizacyjnej. Dziedzina aplikacyjna jest wpierw modelowana w postaci zbioru
obiektów powiązanych różnorodnymi relacjami — model ten odzwierciedla obiekty ze świata
rzeczywistego i zachodzące między nimi powiązania: w systemie sterowania ruchem pocią-
gów wspomniane obiekty reprezentują pociągi, których ruch jest monitorowany, w systemie
zarządzania giełdą obiekty transakcyjne reprezentują klientów kupujących i sprzedających
akcje. W podobny sposób modelowane są koncepcje dotyczące dziedziny realizacyjnej: frag-
ment kodu, reprezentujący ruch pociągu czy transakcję finansową, jest obiektem wchodzą-
cym w skład tej dziedziny. Na gruncie podejścia zorientowanego obiektowo dokonuje się za-
tem transformacja modelu dziedziny aplikacyjnej w model dziedziny realizacyjnej. W tym
ujęciu proces tworzenia oprogramowania przekłada się na zespół aktywności niezbędnych do
postrzegania i opisywania tworzonego systemu jako zbioru modeli odnoszących się bezpośrednio
do problemów, których rozwiązania oczekują użytkownicy tego systemu. Modelowanie
oraz koncepcję wspomnianych obiektów opiszemy szczegółowo w rozdziale 2. „Modelowanie
w języku UML".

1.2.2. Rozwiązywanie problemów


Nieodłącznym aspektem inżynierii jest rozwiązywanie problemów. Inżynierowie poszukują
rozwiązań problemów przeważnie metodą prób i błędów, oceniając dostępne warianty tych
rozwiązań w sposób empiryczny, bo dysponują ograniczonymi zasobami i niekompletną wie-
dzą. Każda metoda inżynierska, w najprostszym ujęciu, daje się przedstawić jako kombinacja
pięciu kroków:

1. Sformułowanie problemu,
2. Analiza problemu,
3. Poszukiwanie dostępnych rozwiązań,
4. Wybór odpowiedniego rozwiązania,
5. Specyfikacja rozwiązania.
1.2. Czym jest inżynieria oprogramowania? 41

Inżynieria oprogramowania jest procesem inżynierskim, nie algorytmicznym: wymaga


eksperymentowania, wielokrotnego wykorzystywania rozwiązań wzorcowych i zapewnienia
przyrostowej ewolucji systemu w kierunku postaci akceptowalnej przez klientów.
Zorientowane obiektowo tworzenie oprogramowania obejmuje sześć następujących
aktywności: zbieranie wymagań, analizę, projektowanie systemu, projektowanie obiektów,
ich implementowanie i testowanie. Na etapie zbierania wymagań i analizy programiści, we
współpracy z klientami, formułują problem i budują model dziedziny aplikacyjnej; korespon-
duje to z punktami 1. i 2. przedstawionego scenariusza. W czasie projektowania systemu
problem dekomponowany jest na serię mniejszych podproblemów, ustalane są też ogólne
strategie projektowe; projektowanie obiektów sprowadza się do rozpoznawania repertuaru
szczegółowych rozwiązań każdego podproblemu i wyboru rozwiązania najbardziej odpo-
wiedniego — otrzymujemy w ten sposób model dziedziny realizacyjnej. Ten krok stanowi
odpowiednik punktów 3. i 4. wspomnianego scenariusza. Implementacja to proces urealnie-
nia systemu poprzez translację modelu dziedziny realizacyjnej na jego wykonywalną repre-
zentację, co odpowiada ostatniemu, 5. punktowi.
Tym, co wyróżnia inżynierię oprogramowania spośród innych dyscyplin inżynierskich,
jest intensywność zmian, jakich doznawać mogą (i faktycznie doznają) obie dziedziny — apli-
kacyjna i realizacyjna — w trakcie rozwiązywania problemu. Z tworzeniem oprogramowania
nieodłącznie związane są czynności zmierzające do oceny adekwatności danego modelu. Na
etapie analizy model dziedziny aplikacyjnej porównywany jest z realiami, w jakich działają
klienci, co skutkować może jego drastyczną nieraz zmianą; na etapie weryfikowania projektu
model dziedziny realizacyjnej konfrontowany jest z celami projektu, zaś w czasie testowania
sam system weryfikowany jest w kontekście modelu dziedziny realizacyjnej, który może się
zmieniać wskutek pojawiania się nowych technologii. Wreszcie, na szczeblu zarządzania pro-
jektem menedżerowie porównują swoje modele — czyli harmonogram i budżet — z realiami,
czyli terminowością dostarczania rozwiązań cząstkowych i dynamiką wykorzystywania zasobów.

1.2.3. Pozyskiwanie wiedzy


Jednym z błędów często popełnianych przez programistów i menedżerów jest przyjmowanie
założenia, iż stopniowe pozyskiwanie wiedzy niezbędnej do stworzenia konkretnego systemu
jest procesem linearnym. Błąd ten nie jest jednak ich wynalazkiem ani też nie są oni w swym
błędnym przekonaniu odosobnieni: w XVII wieku ukazała się książka3, której autor zaleca
utrwalanie wierszy (w języku niemieckim) poprzez sączenie ich tekstu (w sensie dosłownym
— przez lejek umieszczony w uchu!) do głów studentów, przez 6 godzin dziennie. Ów nie-
zwykły pomysł zasadzał się na powszechnie panującym przekonaniu, iż umysł ludzki począt-
kowo jałowy może być wypełniany treścią w sposób linearny — za pomocą zmysłów treść ta
dociera do mózgu, gdzie jest akumulowana i odpowiednio interpretowana. K. Popper nazywa
ów model linearnej akwizycji „teorią umysłu pojemnika" (bucket theory of the mind)-, jednym
z wielu błędów tej teorii ([Popper, 1992]) jest założenie, że wiedza pojmowana jest jako ist-
nienie rzeczy, które mogą stopniowo napełniać wspomniany pojemnik — im pełniejszy, tym
bogatsza staje się nasza wiedza.

3
Georg Philipp Harsdórffer (1607 - 1658), Poetischer Trichter, die teutsche Dicht- und Reimkunst,
ohn Behuf der lateinischen Sprache, in 6 Stunden einzugiefien, Nuernberg, 1630.
42 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

Pozyskiwanie wiedzy nie jest jednak procesem linearnym: pojawienie się nowego faktu
czy nowej informacji może postawić pod znakiem zapytania lub po prostu przekreślić całą
dotychczasową wiedzę, jaką nabyliśmy podczas uczenia się rozumienia naszego systemu. I jeśli
nawet owo rozumienie zostało już odzwierciedlone w postaci dokumentacji i kodu źródło-
wego („kod systemu jest gotowy w 90%, resztę napiszemy w przyszłym tygodniu"), musimy
być mentalnie przygotowani na to, że pisanie dokumentacji czy (co ważniejsze) tworzenie
kodu systemu będziemy musieli rozpocząć od zera. A to w istotny sposób przekłada się na
zbiór aktywności i ich wzajemnych interakcji, jakie zdefiniowaliśmy w związku z tworzeniem
wspomnianego systemu. Na gruncie inżynierii oprogramowania odpowiednikiem teorii „umysłu
pojemnika" jest sekwencyjny model kaskadowy, zgodnie z którym poszczególne kroki zwią-
zane z tworzeniem systemu realizowane są w sposób sekwencyjny.
Istnieje kilka różnych technik i metodologii zmierzających do unikania krępującej se-
kwencyjności modelu kaskadowego — i tak na przykład istotą programowania sterowanego
ryzykiem jest przewidywanie zawczasu problemów, jakie mogą pojawić się w późnych sta-
diach realizacji projektu; dokonuje się tego poprzez identyfikowanie komponentów obarczonych
wysokim ryzykiem. Metodologia programowania sterowanego problemami zmierza natomiast
do całkowitego wyeliminowania narzuconej a priori linearności. Każda z poszczególnych
aktywności związanych z tworzeniem systemu — analiza, projekt systemu, projekt obiektów,
implementowanie, testowanie i wdrażanie — ma mniejszy lub większy wpływ na pozostałe
i na przykład na gruncie programowania sterowanego problemami wszystkie one realizo-
wane są równolegle. Takie nielinearne modele, choć bardziej adekwatne do realiów, mają tę
niedogodność, że trudno się nimi zarządza.

1.2.4. Racjonalizacja
Opisując pozyskiwanie wiedzy i jej ewoluowanie, z konieczności stajemy na pozycji mniej
pewnej w porównaniu z opisywaniem rzeczywistego, istniejącego systemu. Podręczniki wsze-
lakich dyscyplin matematycznych pełne są dowodów, rzadko jednak napotkać można w nich
wzmiankę na temat ich genezy, na temat inspiracji, jakie skierowały myślenie autora w tę czy
inną stronę. Dzieje się tak chyba dlatego, że matematycy nie uważają tego aspektu sprawy za
szczególnie istotny: gdy określi się zbiór pojęć pierwotnych, zbiór aksjomatów i reguł dedukcji,
sam dowód staje się kwestią wręcz automatycznej konstrukcji.
Inżynierowie programiści znajdują się w sytuacji cokolwiek trudniejszej. W przeciwień-
stwie do matematyków, obracających się w komfortowych środowisku ustalonych aksjo-
matów, muszą stawiać czoła nieustannym zmianom. I jeżeli nawet będą mieć to szczęście, że
modele dziedziny aplikacyjnej w miarę ustabilizują się po pewnych czasie, to na pewno modele
dziedziny realizacyjnej gwarantują borykanie się z nieustanną płynnością. Błędy projektowe
i implementacyjne, bezlitośnie obnażane w trakcie testowania, oraz problemy związane
z użytecznością objawiające się przy pierwszym kontakcie użytkowników z nowymi elemen-
tami systemu wymagają często nawet drastycznych zmian w modelu dziedziny realizacyjnej.
Zmiany takie mogą być również wymuszane przez postęp technologiczny: nowa kategoria
długożyciowych akumulatorów oraz nowe osiągnięcia w zakresie szybkiej komunikacji bez-
przewodowej z naturalnych względów wymagają zrewidowania przyjętych wcześniej kon-
cepcji projektowych przenośnego terminala.
1.3. Podstawowe koncepcje inżynierii oprogramowania 43

Nowości technologiczne często stwarzają nowe możliwości w zakresie formułowania


wymagań funkcyjnych i pozafunkcyjnych. Jednym z typowych zadań programistów jest wła-
śnie unowocześnianie istniejących systemów stosownie do możliwości stwarzanych przez nowe
technologie. By podołać temu zadaniu, nie wystarczy rozumienie jego zachowania i aktualnej
struktury komponentów: konieczne jest także poznanie i zrozumienie kontekstu, w jakim
podejmowane były decyzje projektowe determinujące taką, a nie inną ich bieżącą postać.
Ta dodatkowa wiedza nazywana jest racjonalizacją systemu.
Pozyskiwanie tej wiedzy i jej efektywne wykorzystywanie nie są kwestiami banalnymi.
Po pierwsze, każda decyzja projektowa to zwykle nic innego, jak dokonanie wyboru spośród
wielu konkurencyjnych możliwości: wybór ten musi być dokładnie rozważony, oszacowany
i uzasadniony. W konsekwencji racjonalizacja reprezentuje znacznie większy zasób infor-
macji niż modele poszczególnych aspektów dziedziny realizacyjnej. Po drugie, informacja
składająca się na racjonalizację zwykle nie jest osiągalna w jawnej postaci. Programiści często
podejmują swe decyzje, bazując na intuicji i doświadczeniu, bez formalnego rozważania
kryteriów oceny poszczególnych wariantów rozwiązania — pytani o uzasadnienie takiej czy
innej decyzji, niekiedy muszą spędzić sporo czasu, by uzasadnienie to wyartykułować w sposób
zrozumiały dla innych. W obliczu dynamiki zmieniającego się systemu takie uzasadnienia są
jednak koniecznością.

1.3. Podstawowe koncepcje inżynierii oprogramowania


Zaprezentowaliśmy właśnie wysokopoziomowe spojrzenie na inżynierię oprogramowania
z perspektywy modelowania, rozwiązywania problemów, pozyskiwania wiedzy i racjonalizacji.
W tej sekcji wyjaśnimy znaczenie podstawowych terminów oraz opiszemy najważniejsze kon-
cepcje, jakie przewijać się będą przez całą treść książki4. A zatem projekt, którego celem jest
stworzenie systemu informatycznego, urzeczywistniany jest poprzez różne aktywności. Każda
aktywność obejmuje wykonywanie pewnej liczby zadań. Zadania powodują zużywanie prze-
znaczonych na nie zasobów, w zamian dostarczając produkty. Produktem może być system,
model lub dokument, n a t o m i a s t p r z y k ł a d a m i zasobów są uczestnicy, czas i sprzęt. Zależności
te przedstawiono symbolicznie na rysunku 1.1. Każdy prostokąt reprezentuje tu pewną kon-
cepcję, zaś przebiegające między prostokątami linie odzwierciedlają różne zależności mię-
dzy nimi — i tak na przykład romb symbolizuje agregację: projekt obejmuje pewną liczbę
aktywności, z których każda obejmuje pewną liczbę zadań. Trójkąty symbolizują generali-
zację: uczestnicy, czas i sprzęt to szczególne przykłady zasobów. Nieprzypadkowo ilu-
stracja ta ma formę notacji języka UML (Universal Modeling Language), ponieważ w tej
książce używać będziemy tej notacji do reprezentowania licznych modeli — jest ona na tyle
intuicyjna, że staje się zrozumiała nawet bez znajomości kompletnej semantyki UML: dia-
gramy UML mogą być wykorzystywane nawet podczas dialogu z klientem, mimo iż ten
może nie mieć pojęcia o istnieniu tego języka. A propos wspomnianej semantyki — opi-
sujemy ją ze szczegółami w rozdziale 2. „Modelowanie w języku UML".

4
Staramy się przy tym zachować jak największą zgodność z definicjami standardów IEEE w zakre-
sie inżynierii oprogramowania [IEEE Std. 610.12-1990].
44 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

Rysunek 1.1. Podstawowe koncepcje inżynierii oprogramowania przedstawione w formie diagramów klas
UML [OMG, 2009]

1.3.1. Uczestnicy i role


Tworzenie oprogramowania odbywa się przy udziale wielu ludzi, obdarzonych różnymi kwa-
lifikacjami i przedstawiających różne kierunki własnych zainteresowań: klienci zamawiają
system i za niego płacą, programiści fizycznie tworzą ten system, zaś menedżerowie projektu
sprawują kontrolę nad harmonogramem i budżetem oraz koordynują interakcje między pro-
gramistami a klientem. Użytkownicy systemu wykorzystują go do celów, które w ogóle prze-
sądziły o jego stworzeniu. Każdą osobę zaangażowaną w realizację projektu nazywać będziemy
jego uczestnikiem. Zbiór rozmaitych zakresów odpowiedzialności w związku z projektem lub
systemem określać będziemy mianem roli. Każda rola związana jest z pewnym zbiorem zadań
i przypisana określonemu uczestnikowi. Jednemu uczestnikowi przypisać można wiele ról.
By lepiej zrozumieć powyższe pojęcia, przeanalizujmy opis systemu o nazwie Ticket
"-•Di s t r i butor, który fizycznie jest komputerowym automatem do sprzedaży biletów.

TicketDistributor ma być automatem sprzedającym bilety na pociąg. Podróżny ma mieć


możliwość wyboru między biletem na pojedynczą podróż, na wiele podróży i karnetem ważnym
jeden dzień lub cały tydzień. TicketDistributor ma wyliczać cenę żądanego biletu, bazując na
danych geograficznych określających cel podróży, z uwzględnieniem ewentualnej ulgi wynika-
jącej z młodego wieku podróżnego. TicketDistributor musi radzić sobie z rozlicznymi sytu-
acjami wyjątkowymi, na przykład niedokończonymi i anulowanymi transakcjami, niemożno-
ścią wydania reszty, wyczerpaniem zasobów (taśmy biletowej) czy awariami zasilania.
1.3. Podstawowe koncepcje inżynierii oprogramowania 45

G d y p o t r a k t o w a ć Ti cketDi stri butor j a k o p r o j e k t s y s t e m u i n f o r m a t y c z n e g o , m o ż n a


w jego kontekście zdefiniować przykładowe role, co zrobiliśmy w tabeli 1.1.

Tabela 1.1. Przykładowe role w projekcie systemu Ti cketDi s t r i b u t o r

Rola Zakres odpowiedzialności Przykłady


Klient Dostarczenie wysokopoziomowego Kompania kolejowa zamawiająca
zestawu wymagań dla systemu, system T i c k e t D i s t r i b u t o r
zdefiniowanie w a r u n k ó w
brzegowych projektu (daty
ukończenia realizacji, budżetu,
kryteriów jakościowych).

Użytkownik Dostarczenie wiedzy na temat Podróżny


dziedziny aplikacyjnej, czyli
zakładanych zachowań podróżnego
kupującego bilet w automacie.
Zauważmy, że role klienta
i użytkownika przypisywane są
zwykle różnym osobom.

Menedżer Organizacja pracy: zatrudnienie Alicja (szef)


personelu, przyporządkowanie
zadań poszczególnym pracownikom,
monitorowanie postępu pracy
każdego z nich, zapewnienie im
szkoleń i treningów, ogólna kontrola
nad zasobami dostarczanymi przez
klienta.

Specjalista Zapewnienie użyteczności systemu. Zoe (specjalista od interakcji


od czynnika ludzkiego „człowiek-komputer")

Programista Konstrukcja systemu: specyfikacja, John (analityk), Marc (koder),


projekt, implementacja i testowanie. Zoe (tester) 5
W p r z y p a d k u b a r d z o dużych
systemów role poszczególnych
programistów podlegają bardziej
szczegółowemu zróżnicowaniu.

Dokumentalista Sporządzenie d o k u m e n t a c j i John


czytelnej i zrozumiałej dla klienta,
w drodze negocjacji z menedżerami,
programistami i użytkownikami
na temat stopnia zrozumiałości
systemu.

5
T i c k e t D i s t r i b u t o r jest systemem stosunkowo prostym, dlatego Zoe pełni podwójną rolę: specja-
listy ds. czynnika ludzkiego i testera, podobnie podwójna rola — analityka i dokumentalisty — przy-
pisana jest Johnowi.
46 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

1.3.2. Systemy i modele


Określenia system używać będziemy w znaczeniu kolekcji połączonych ze sobą części. Mo-
delowanie to sposób radzenia sobie ze złożonością systemów (i wszelkich zagadnień w ogólno-
ści) poprzez ignorowanie rzeczy nieistotnych w danym kontekście. Pisząc w tej książce model,
mamy na myśli dowolną abstrakcję systemu. Wspomniany wcześniej Ti cketDi s t r i butor
jest przykładem systemu, zaś jego modelami są: „niebieski podręcznik" (blueprint), schematy
jego połączeń elektrycznych i modele obiektów składających się na jego oprogramowanie.
Zauważmy, że projekt systemu sam jest systemem w przyjętym powyżej znaczeniu i jako taki
sam stanowić może przedmiot modelowania — harmonogram, budżet i kolejne terminy de-
adline są właśnie jego modelami.

1.3.3. Produkty
Produkt to coś użytecznego, co powstaje w trakcie realizacji systemu, na przykład dokument
lub fragment programu, przeznaczony dla klienta lub na wewnętrzne potrzeby projektu — tę
drugą kategorię nazywa się często produktem wewnętrznym, należący natomiast do kategorii
pierwszej produkt określa się jako produkt docelowy. Te ostatnie definiowane są typowo
jeszcze przed rozpoczęciem projektu, na podstawie kontraktu wiążącego wykonawcę z klien-
tem. W tabeli 1.2 przedstawiliśmy krótki opis produktów związanych z systemem Ticket
"-•Distributor.

Tabela 1.2. Przykładowe produkty projektu Ti cketDi s t r i b u t o r

Produkt Typ Opis


Specyfikacja Produkt Specyfikacja opisuje system z perspektywy jego użytkowników.
docelowy Stanowi swoisty k o n t r a k t między p r o j e k t e m a klientem.
Specyfikacja systemu Ti cketDi s t r i butor to szczegółowy opis
systemu w taki sposób, w jaki widzieć go będą korzystający
z niego podróżni.

Podręcznik Produkt Podręcznik serwisowy systemu Ti cketDi s t r i b u t o r


serwisowy docelowy przeznaczony jest dla personelu technicznego kompanii
kolejowej, odpowiedzialnego za zainstalowanie i konfigurowanie
tegoż systemu. Elementem wspomnianego konfigurowania
może być zmiana ustawień spowodowana zmianami w zakresie
cen biletów czy struktury połączeń kolejowych między
poszczególnymi stacjami.

Raport o statusie Produkt Jest to bieżący raport o zadaniach, które już zostały zakończone,
projektu wewnętrzny i tych właśnie wykonywanych. Taki raport sporządzany jest
na użytek szefa (Alicji) i rzadko u d o s t ę p n i a n y klientom
(kompanii kolejowej).

Podręcznik Produkt Planowanie testów i dokumentowanie wyników testów już


testera wewnętrzny przeprowadzonych to domena testera (Zoe). Dokumenty takie
zawierają opis znanych defektów, znalezionych w systemie
Ti cketDi s t r i butor, i raport na temat stanu ich naprawiania.
Tych dokumentów klient zazwyczaj nie ogląda.
1.3. Podstawowe koncepcje inżynierii oprogramowania 47

1.3.4. Aktywności, zadania i zasoby


Aktywność jest zbiorem zadań ukierunkowanych na osiągnięcie pewnego celu. Przykładowo
zbieranie wymagań jest aktywnością zmierzającą do zdefiniowania przez klienta jego ocze-
kiwań względem powstającego systemu. Dostarczanie to aktywność, której celem jest zain-
stalowanie systemu w lokalizacji docelowej. Zarządzanie to aktywność polegająca na monito-
rowaniu i kontrolowaniu projektu pod względem zgodności z założonymi celami (terminy,
jakość, budżet). Konkretna aktywność może mieć strukturę złożoną i składać się z kilku prost-
szych aktywności, na przykład aktywność dostarczania obejmuje dwie prostsze aktywności
— zainstalowanie oprogramowania i przeszkolenie personelu. Aktywności nazywane są nie-
kiedy fazami projektu.
Zadanie reprezentuje niepodzielną jednostkę pracy, która może być odrębnie zarządzana.
Menedżer przydziela zadanie programiście, ten zadanie to wykonuje, a menedżer monito-
ruje stan zaawansowania wykonania. Zadania powodują konsumowanie zasobów oraz gene-
rowanie produktów i zwykle zależne są od produktów generowanych przez inne zadania.
Zasobami nazywamy wszelkie dobra wykorzystywane i (lub) zużywane do wykonania
pewnego zadania. Zasobami są czas, sprzęt i praca. Podczas planowania projektu menedżer
dokonuje rozdziału realizacji projektu na poszczególne zadania i z każdym z nich wiąże okre-
śloną porcję zasobów.
W tabeli 1.3 opisaliśmy przykładowe aktywności, zadania i zasoby charakterystyczne dla
inżynierii oprogramowania.

Tabela 1.3. Przykłady aktywności, zadań i zasobów związanych z projektem Ti cketDi s t r i b u t o r

Przykład Typ Opis


Zbieranie wymagań Aktywność Obejmuje uzyskiwanie, od klientów i użytkowników,
i weryfikowanie wymagań oraz wiedzy na temat
dziedziny aplikacyjnej. Owocem zbierania wymagań
jest specyfikacja (patrz tabela 1.2).

Realizacja przypadku Zadanie Zadanie to, przypisane testerowi (Zoe), koncentruje


testowego „nie m a m się na zweryfikowaniu zachowania systemu
drobnych" Ti cketDi s t r i b u t o r w sytuacji, gdy bieżący stan
zasobów nominałów płatniczych w automacie
uniemożliwia prawidłowe wydanie reszty podróżnemu.
Konstruowanie przez Zoe przypadku testowego (które
w opisywanym tu sensie jest aktywnością) obejmuje
określenie środowiska testowego, sekwencji danych
wprowadzanych do automatu i jego oczekiwanej reakcji.

Ocena adekwatności przypadku Zadanie Zadanie to, przydzielone Johnowi (specjaliście od


testowego „Szybka pomoc" pod czynnika ludzkiego), koncentruje się na wykrywaniu
względem użyteczności systemu elementów deprecjonujących wspomnianą użyteczność.
pomocy dla podróżnych

Baza danych taryf i cennika Zasób Baza ta zawiera przykładowy plan taryfowy, wraz
z przykładową siecią połączeń. Klient będzie ją oceniać
pod względem zgodności ze swymi wymaganiami,
a następnie wykorzystywać do testowania powstających
fragmentów systemu.
48 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

1.3.5. Wymagania funkcyjne i pozafunkcyjne


Wymagania to w istocie zestaw cech, jakie musi posiadać tworzony system. Wymagania funk-
cyjne określają funkcje, które system ten ma realizować. Pod określeniem wymagań po-
zafunkcyjnych rozumiemy rozmaite ograniczenia narzucone na system, które jednak nie odno-
szą się bezpośrednio do realizowanych przez niego funkcji.
I tak na przykład wymagania Podróżni muszą być w stanie kupować bilety lub Dla po-
dróżnych musi być dostępna informacja taryfowa należą do grupy wymagań funkcyjnych; do
wymagań pozafunkcyjnych można zaliczyć te w rodzaju Czas oczekiwania podróżnego na
odpowiedź automatu musi być krótszy niż sekunda czy Kolorystyka interfejsu użytkownika
musi być spójna z kolorystyką typową dla kompanii. Wymagania pozafunkcyjne mogą także
obejmować różnorodne aspekty realizacyjne systemu, takie jak konkretna platforma sprzętowa,
określona infrastruktura bezpieczeństwa, sposób radzenia sobie systemu z awariami i własnymi
usterkami czy sposób zapewnienia kompatybilności z systemem wcześniej funkcjonującym.

1.3.6. Notacje, metody i metodologie


Notacja to zestaw reguł reprezentowania modelu w sposób graficzny lub tekstowy. Alfabet
rzymski jest notacją przeznaczoną do reprezentowania słów języka. UML (Unified Modeling
Language [OMG, 2009]) — notacja, której konsekwentnie używamy w tej książce — jest języ-
kiem przeznaczonym do reprezentowania modeli zorientowanych obiektowo. Inżynieria
oprogramowania od zawsze wykorzystywała rozmaite notacje, na długo przedtem, zanim po-
jawiły się techniki obiektowe. Diagramy przepływu danych [De Marco, 1978] to notacja re-
prezentacji systemu w kategoriach źródeł i ujść danych oraz transformacji danych. Notacja
Z [Spivey, 1989] opiera się na koncepcji postrzegania systemu w kategoriach teorii mnogości.
Metoda to powtarzalna technika określająca ciąg kroków obejmujących rozwiązywanie
konkretnego problemu. Konkretny przepis z książki kucharskiej jest metodą przyrządzania
określonego specjału (na przykład kremu czekoladowego). Każdy algorytm sortowania jest
metodą porządkowania elementów listy. Zarządzanie racjonalizacją jest metodą uzasadniania
zmian wprowadzanych do projektu. Zarządzanie konfiguracją to metoda śledzenia zmian.
Metodologią nazywamy kolekcję metod związanych z określoną klasą problemów, wraz
z określeniem, jak i kiedy należy używać każdej z tych metod. Książka kucharska poświęcona
owocom morza jest metodologią przyrządzania potraw tej właśnie kategorii, o ile zawiera
wskazówki dotyczące użycia określonych składników i radzenia sobie z sytuacjami, gdy — jak
na złość — brakuje konkretnego składnika. Metodologia Royce'a [Royce, 1998], metodologia
OMT (Object Modeling Technique OMT [Rumbaugh i in., 1991]), metodologia Boocha [Booch,
1994], Catalysis [D'Souza i Wills, 1999] to zorientowane obiektowo metodologie tworzenia
oprogramowania.
Metodologie procesu tworzenia oprogramowania dekomponują ten proces na rozmaite
aktywności, przykładowo OMT dostarcza metody dla trzech aktywności: analizy, skupiającej
się na formalizacji wymagań dla systemu w postaci modelu obiektowego, projektowania sys-
temu, rozumianego jako podejmowanie strategicznych decyzji, oraz projektu obiektów, trans-
formującego model stworzony na etapie analizy w model obiektowy, zdatny do zaimplemen-
towania. Częścią metodologii OMT jest założenie, że wymagania zostały już zdefiniowane, nie
1.4. Aktywności inżynierii oprogramowania 49

dostarcza ona przeto żadnych metod zbierania wymagań. Metodologia jednolitego procesu
(Unified Software Development Process)1' także obejmuje aktywność analizy, lecz w przeciwień-
stwie do OMT aktywności projektowania systemu i projektu obiektów połączone są w po-
jedynczą aktywność zwaną projektowaniem. Z kolei metodologia Catalysis, stosując tę samą
notację, co jednolity proces, skupia się w większym stopniu na wielokrotnym wykorzystywaniu
projektów i kodu, dostarczając odpowiednich wzorców i narzędzi do tego celu. Niezależnie
jednak od opisanych różnic, wszystkie wymienione metodologie stworzone zostały w tym
samym celu — w celu uporania się ze złożonością tworzonych systemów.
W książce tej prezentujemy metodologię tworzenia złożonych systemów w obliczu ko-
niecznych, dynamicznych zmian. W czasie prowadzonych przez nas kursów i badań ([Bruegge,
1992], [Bruegge i Coyne, 1993], [Bruegge i Coyne, 1994], [Coyne i in., 1995]) zaadaptowaliśmy
i udoskonaliliśmy wiele metod zaczerpniętych z różnych źródeł. W związku z aktywnościami
modelowania dziedziny aplikacyjnej, czyli zbieraniem wymagań i analizą, opisujemy metody
podobne do przedstawionych w OOSE [Jacobson i in. 1992]. Dla aktywności związanych z mo-
delowaniem dziedziny realizacyjnej, takich jak projektowanie systemu i projekt obiektów,
przewidzieliśmy zorientowane obiektowo aktywności podobne do znanych z OMT. Wśród ak-
tywności związanych z zarządzaniem zmianami szczególną uwagę poświęcamy zarządzaniu
racjonalizacją, wzorując się na badaniach racjonalizacji projektowej [Moran & Carroll, 1996],
oraz zarządzaniu konfiguracją w sposób zbliżony do opisanego [Babich, 1986] w związku
z konserwowaniem dużych systemów.

1.4. Aktywności inżynierii oprogramowania


W tej sekcji przedstawimy ogólny obraz technicznych aktywności związanych z obiektowym
podejściem do tworzenia oprogramowania. Istotą tych aktywności jest radzenie sobie ze zło-
żonością problemów poprzez konstruowanie i weryfikowanie modeli dziedziny aplikacyjnej
i samego systemu. Aktywności te obejmują:

• zbieranie wymagań (opisywane w sekcji 1.4.1),


• analizę (sekcja 1.4.2),
• projektowanie systemu (sekcja 1.4.3),
• projekt obiektów (sekcja 1.4.4),
• implementowanie (sekcja 1.4.5),
• testowanie (sekcja 1.4.6).

Na rysunku 1.2 przedstawiliśmy ogólny obraz zależności występujących pomiędzy wy-


mienionymi aktywnościami i generowanymi przez nie produktami. W rozdziałach 14. „Zarzą-
dzanie projektem" i 15. „Cykl życiowy oprogramowania" dyskutujemy bardziej szczegółowo
aktywności związane z modelowaniem i planowaniem.

6
Patrz sekcja 15.4.2 — przyp. tłum.
50 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

Rysunek 1.2. Ogólny obraz aktywności związanych z obiektowo zorientowanym tworzeniem oprogra-
mowania oraz produktów generowanych przez te aktywności. Powyższy diagram odzwierciedla jedynie
logiczne zależności między produktami; obiektowo rozumiana inżynieria oprogramowania jest dyscy-
pliną iteracyjną — niektóre aktywności mogą występować w wielu instancjach i równolegle z innymi

1.4.1. Zbieranie wymagań


N a etapie zbierania w y m a g a ń klient i programiści definiują przeznaczenie systemu. Rezultatem
tej a k t y w n o ś c i jest opis s y s t e m u w kategoriach a k t o r ó w i p r z y p a d k ó w użycia. A k t o r z y r e p r e -
z e n t u j ą byty z e w n ę t r z n e uwikłane w interakcję z systemem, m i ę d z y i n n y m i role u ż y t k o w n i k ó w ,
1.4. Aktywności inżynierii oprogramowania 51

zewnętrznych komputerów (serwera sieciowego, centralnego komputera bankowego) czy śro-


dowiska (którym może być na przykład proces chemiczny). Przypadkami użycia są ogólne
sekwencje zdarzeń odzwierciedlające wszelkie możliwe akcje występujące między aktorami
a systemem w kontekście określonego aspektu jego funkcjonalności. W tabeli 1.4 opisaliśmy
przypadki użycia naszego przykładowego systemu Ti cketDi s t r i butor. Samo zbieranie wyma-
gań, wraz z przypadkami użycia oraz uwzględnieniem podziału na wymagania funkcyjne i po-
zafunkcyjne, opiszemy szczegółowo w rozdziale 4. „Zbieranie wymagań".

Tabela 1.4. Przykładowy przypadek użycia— PurchaseOneWayTicket

Nazwa przypadku testowego PurchaseOneWayTi cket

Aktor uczestniczący Traveler

Sekwencja zdarzeń 1. Travel er wybiera strefę, w której zlokalizowana jest stacja docelowa
2. Ti cketDi s t r i b u t o r wyświetla cenę biletu.
3. Travel e r wprowadza do automatu środki płatnicze w wysokości
nie niższej niż cena biletu.
4. Ti cketDi s t r i butor drukuje żądany bilet i wydaje ewentualną resztę.
Warunek wstępny Travel e r staje przed automatem zlokalizowanym na stacji początkowej
lub dowolnej stacji pośredniej.

Warunek końcowy Travel e r jest w posiadaniu żądanego biletu i zwróconej nadpłaty.

Wymagania jakościowe Jeżeli transakcja nie została ukończona, a bezczynność Travel era trwa
dłużej niż minutę, Ti cketDi s t r i butor zwraca ewentualną gotówkę
wprowadzoną już przez Travel e r a w ramach bieżącej transakcji.

1.4.2. Analiza
Na etapie analizy programiści dążą do stworzenia modelu systemu — poprawnego, kom-
pletnego, spójnego i wolnego od niejednoznaczności. Dokonują transformacji przypadków
użycia, zdefiniowanych podczas zbierania wymagań, w model obiektowy, który jest komplet-
nym opisem systemu. Ewentualne przejawy niejednoznaczności i niespójności w modelu
przypadków użycia wyjaśniane są w drodze negocjacji z klientem. Rezultatem analizy jest mo-
del systemu wzbogacony o atrybuty, operacje i skojarzenia. Model ten daje się opisać w kate-
goriach struktury systemu i jego dynamicznego współdziałania. Na rysunku 1.3 przedstawiamy
przykładowy model dynamiczny systemu Ti cketDi s t r i butor, zaś na rysunku 1.4 — jego
model obiektowy.
Etap analizy, z uwzględnieniem modeli obiektowych, opisujemy z detalami w roz-
dziale 5. „Analiza wymagań", zaś szczegóły notacji UML wykorzystywanej do reprezento-
wania obiektów wyjaśniamy w rozdziale 2. „Modelowanie w języku UML".

1.4.3. Projekt systemu


W czasie projektowania systemu programiści definiują cele projektu i dekomponują system
na mniejsze podsystemy, z których każdy realizowany będzie przez odrębny zespół. Programi-
ści dokonują także wyboru strategii budowania systemu, m.in. platformy sprzętowej i systemu
52 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

Rysunek 1.3. Model dynamiczny systemu Ti cketDi s t r i butor (diagram sekwencyjny UML). Na dia-
gramie tym uwidoczniliśmy obiekty będące elementami przypadku użycia PurchaseOneWayTi cket
oraz interakcje zachodzące między systemem a aktorem Travel e r w ramach tego przypadku

Rysunek 1.4. Model obiektowy systemu Ti cketDi s t r i b u t o r (diagram klas UML). W ramach przy-
padku użycia PurchaseOneWayTi cket aktor Traveler inicjuje transakcję, reprezentowaną przez obiekt
Transaction, której rezultatem jest wyprodukowanie obiektu Ticket. Bilet reprezentowany przez ten
obiekt ważny jest tylko w strefie reprezentowanej przez obiekt Zone. W ramach wspomnianej transakcji
system kontroluje jej bilans (reprezentowany przez obiekt Bal ance), zliczając narastająco wartość wpro-
wadzanych do automatu monet (Coi n) i porównując tę wartość z ceną biletu (Bi 11)

operacyjnego, w środowisku których działać ma docelowo projektowany system, a także tech-


nologii trwałego przechowywania danych, globalnej kontroli przepływu sterowania, reguł polityki
dostępu i sposobu obsługi warunków granicznych. W rezultacie system jawi się jako klarowny
opis każdej z tych strategii, jako zespół podsystemów, a także jako diagram wdrożeniowy w posta-
ci odwzorowania poszczególnych jego aspektów na komponenty sprzętowe i programowe. Mimo
iż zarówno analiza, jak i projekt systemu skutkują powstawaniem modeli tworzonego systemu,
to jedynie analiza obejmuje encje zrozumiałe dla klienta: projekt systemu to operowanie bardziej
zaawansowanymi i ulepszonymi modelami, zawierającymi elementy wykraczające poza możli-
wość zrozumienia (i zainteresowanie) klienta. Na rysunku 1.5 widzimy przykład dekompozycji
systemu Ti cketDi s t r i butor. Projekt systemu i powiązane z nim koncepcje opisujemy szcze-
gółowo w rozdziałach 6. „Projektowanie systemu — dekompozycja na podsystemy" i 7. „Pro-
jekt systemu: realizacja celów projektowych".
1.4. Aktywności inżynierii oprogramowania 53

Rysunek 1.5. Przykład dekompozycji systemu Ti cketDi s t r i butor (diagram klas UML — pakiety repre-
zentują podsystemy, a linie przerywane — zależności między nimi). Podsystem Travel e r l n t e r f a c e
odpowiedzialny jest za kolekcjonowanie informacji wejściowych i sprzężenie zwrotne z podróżnym
(między innymi wyświetlenie ceny biletu, wydrukowanie biletu, wydanie reszty). Zadaniem podsystemu
Local T a r i f f jest obliczenie ceny biletu na podstawie cennika zapisanego w lokalnej bazie danych.
Podsystem Central Tari f f, zlokalizowany na centralnym komputerze, ma za zadanie utrzymywanie
kopii referencyjnej bazy danych taryfowych; zapewnienie zgodności z tą kopią każdej lokalnej bazy
w poszczególnych egzemplarzach systemu T i c k e t D i s t r i b u t o r jest zadaniem podsystemu Updater

1.4.4. Projektowanie obiektów


W czasie projektowania obiektów programiści definiują obiekty dziedziny realizacyjnej. Za-
daniem tych obiektów jest zniwelowanie luki między modelem zbudowanym na etapie analizy
a platformą sprzętową i programową zdefiniowaną na etapie projektu systemu. Sprowadza się to
do precyzyjnego opisywania interfejsów poszczególnych obiektów i podsystemów, do wyboru
odpowiednich gotowych komponentów („z półki"), do restrukturyzowania modelu obiektowego
w celu osiągnięcia takich celów projektowych jak rozszerzalność oraz zrozumiałość i wreszcie do
optymalizowania tego modelu pod kątem wydajności tworzonego systemu. Owocem tej aktyw-
ności jest szczegółowy model obiektowy opatrzony adnotacjami wyjaśniającymi istniejące
ograniczenia oraz dostarczającymi szczegółowe opisy każdego elementu. Opis projektowania
obiektów, wraz z koncepcjami pokrewnymi, będzie treścią rozdziałów 8. „Projektowanie obiek-
tów: wielokrotne wykorzystywanie rozwiązań wzorcowych" i 9. „Projektowanie obiektów: spe-
cyfikowanie interfejsów".

1.4.5. Implementowanie
Implementowanie to tłumaczenie dziedziny realizacyjnej na kod źródłowy aplikacji. W prak-
tyce oznacza to kodowanie atrybutów i metod każdego obiektu w języku programowania i in-
tegrowanie wszystkich obiektów w funkcjonujący pojedynczy system. Aktywność implemen-
towania wypełnia lukę pomiędzy uszczegółowionym projektem obiektów a kompletnym,
kompilowalnym kodem źródłowym. Czynność mapowania modeli UML w kod źródłowy opi-
szemy szczegółowo w rozdziale 10. „Odwzorowywanie modelu na kod"; zakładamy, że czytel-
nikom nieobce są podstawowe koncepcje programistyczne oraz umiejętność programowania
struktur danych i algorytmów w języku zorientowanym obiektowo, takim jak Java czy C++.
54 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

1.4.6. Testowanie
Testowanie to etap wynajdywania różnic między urzeczywistnionymi fragmentami systemu
a ich modelami, za pomocą specjalnie przygotowanych w tym celu danych wejściowych. I tak
w czasie testowania modułów programiści porównują modele stworzone na etapie projekto-
wania obiektów z każdym zaimplementowanym modelem i podsystemem; testy integracyjne
to weryfikacja prawidłowości połączenia podsystemów w całość, w kontekście modelu stwo-
rzonego na etapie projektowania systemu. Wreszcie, na etapie testowania systemowego na-
stępuje konfrontacja zachowania się systemu w nietypowych i wyjątkowych sytuacjach z mo-
delem stworzonym na etapie zbierania wymagań. Celem testowania jest wykrycie jak największej
liczby usterek i poprawienie ich przed dostarczeniem systemu klientowi. Planowanie poszcze-
gólnych rodzajów testów odbywa się równolegle z innymi fazami tworzenia systemu — i tak
testy systemowe planowane są równolegle ze zbieraniem wymagań i analizą, planowanie testów
integracyjnych towarzyszy projektowaniu systemu, zaś projektowanie obiektów połączone
jest z planowaniem testów modułowych. Zależności te opiszemy dokładniej w rozdziale 11.
„Testowanie".

1.5. Zarządzanie tworzeniem oprogramowania


W tej sekcji opiszemy pokrótce aktywności związane z zarządzaniem projektami informatycz-
nymi. Aktywności te koncentrują się na planowaniu projektu, monitorowaniu jego statusu,
śledzeniu zmian i koordynowaniu wykorzystania zasobów, z intencją dostarczenia wysokiej
jakości produktu na czas i zgodnie z zaplanowanym budżetem. Są one udziałem nie tylko me-
nedżerów, ale także wielu innych uczestników. Obejmują między innymi:

• komunikację (patrz sekcja 1.5.1),


• zarządzanie racjonalizacją (patrz sekcja 1.5.2),
• zarządzanie konfiguracją (patrz sekcja 1.5.3),
• zarządzanie projektem (patrz sekcja 1.5.4),
• cykl życiowy oprogramowania (patrz sekcja 1.5.5).

Gdy gotowy system zostanie przekazany klientowi, rozpoczyna się proces, który nazwać
można ogólnie utrzymaniem lub konserwację tegoż systemu. W książce nie będziemy zajmo-
wać się tą tematykę, ograniczymy się do kilku uwag w tym miejscu. Tradycyjnie konserwację
powierza się zespołowi innemu niż ten, który system wyprodukował; czyni się tak ze względu
na fakt, że jest ona procesem wybitnie sterowanym zachodzącymi zmianami i z definicji od-
dzielonym od tworzenia systemu. Ponieważ jednak współczesne projekty informatyczne także
realizowane są w obliczu dynamicznych zmian, granica między jednym a drugim staje się co-
kolwiek płynna; w rezultacie wiele opisywanych w tej książce aktywności — projektowanie
obiektów, implementowanie, testowanie, zarządzanie racjonalizacją i konfiguracją — realizo-
wanych jest także w fazie konserwacji.
1.5. Zarządzanie tworzeniem oprogramowania 55

1.5.1. Komunikacja
Komunikacja to najbardziej krytyczna i czasochłonna aktywność inżynierii oprogramowania.
Nieporozumienia i przeoczenia często prowadzą do powstawania usterek i opóźnień, kosz-
townych i trudnych do naprawienia w późniejszych fazach. Komunikacja obejmuje wymianę
modeli i dokumentów dotyczących systemu i jego dziedziny aplikacyjnej, raportowanie statusu
produktów i realizowanie sprzężenia zwrotnego dotyczącego ich jakości, formułowanie i ne-
gocjowanie problemów oraz oznajmianie różnorodnych decyzji.
Komunikacja staje się utrudniona ze względu na różnice między kwalifikacjami po-
szczególnych uczestników, na ich rozproszenie geograficzne oraz ze względu na komplek-
sowość, rozmiar i ewoluowanie wymienianej informacji.
Aby sprawnie radzić sobie z tymi trudnościami, uczestnicy mają do dyspozycji wiele
metod i narzędzi, wśród których do najbardziej efektywnych zaliczyć należy wszelkiego ro-
dzaju konwencje: gdy uczestnicy porozumieją się co do notacji reprezentującej informację, co
do narzędzi służących do jej przetwarzania oraz procedur zgłaszania i negocjowania proble-
mów, eliminują tym samym dość spory repertuar potencjalnych źródeł rozmaitych nieporo-
zumień. Przykładem takiej uzgodnionej notacji mogą być diagramy UML, szablony różnych
dokumentów czy schematy nazewnictwa poszczególnych komponentów aplikacji. Do narzę-
dzi tego rodzaju zaliczyć można te z kategorii CASE (Computer Aided Software Engineering
— „wspomagana komputerowo inżynieria oprogramowania") służące do operowania na mo-
delach, edytory tekstów i standardy formatów publikowania informacji. Przykładowymi pro-
cedurami komunikacyjnymi mogą być wzorcowe zasady organizowania, prowadzenia i do-
kumentowania spotkań roboczych, procedury analizowania i oceniania dokumentów czy
procedury inspekcyjne zmierzające do wykrywania defektów w modelach i kodzie źródłowym.
Uzgodnione konwencje nie muszą być „najlepsze iż najlepszych": ważne jest, by były zrozu-
miałe dla wszystkich uczestników i przez wszystkich akceptowane. Zagadnienia związane ze
sprawnym komunikowaniem się opisujemy w rozdziale 13. „Zarządzanie konfiguracją".

1.5.2. Zarządzanie racjonalizacją


Racjonalizacja to szeroko rozumiane usprawiedliwianie każdej podejmowanej decyzji, między
innymi w aspekcie zestawu problemów, rozwiązaniu których miała służyć, jej wyboru spośród
wielu różnych rozwiązań, kryteriów, jakimi kierowali się programiści przy ocenianiu tych roz-
wiązań, czy debat i negocjacji, jakie w związku z tym prowadzili. Racjonalizacja to najważniejsza
informacja, jaką posiadać muszą programiści przystępujący do dokonywania zmian w systemie:
gdy nieoczekiwanie zmienią się jakieś okoliczności czy kryteria, programiści mogą ponownie
rozważyć wszystkie decyzje zależne od tych okoliczności lub kryteriów. Gdy pojawi się nowa
alternatywa, możliwe będzie jej porównanie ze wszystkimi innymi rozwiązaniami, jakich oceny
już wcześniej dokonano. W sytuacji gdy jakaś decyzja zostanie zakwestionowana, stojąca za nią
racjonalizacja staje się sposobem jej obrony.
Niestety, racjonalizacja należy do najbardziej złożonych rodzajów informacji, z którymi
mają do czynienia programiści, jako taka jest więc najtrudniejsza do utrzymywania i aktuali-
zowania. By radzić sobie z tymi trudnościami, programiści dążą do reprezentowania racjona-
lizacji w formie różnorodnych modeli budowanych w rezultacie spotkań oraz dyskusji online
i aktualizowanych stosownie do zachodzących zmian. Zajmiemy się szerzej tą tematyką w roz-
dziale 12. „Zarządzanie racjonalizacją.
56 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

1.5.3. Zarządzanie konfiguracją oprogramowania


Istotą zarządzania konfiguracją jest monitorowanie i kontrolowanie zmian w zakresie pro-
duktów. Zmiany są nieodłącznym elementem tworzenia oprogramowania. Gdy klient żąda
nowych cech aplikacji lub gdy programiści coraz lepiej rozumieją poszczególne aspekty dzie-
dziny aplikacyjnej, mamy do czynienia ze zmianami w wymaganiach; gdy pojawiają się nowe
technologie, zmienia się platforma sprzętowa i (lub) programowa, na której system jest reali-
zowany; wykrywanie i poprawianie błędów podczas testowania oznacza zmiany w samym
systemie. Do niedawna jeszcze wymienione zmiany były raczej typowe dla konserwacji eks-
ploatowanego systemu, jednak współczesne projekty programistyczne doznają rozmaitych zmian
w stopniu bodaj jeszcze większym, wskutek czego z zarządzaniem konfiguracją mamy do czy-
nienia we wszystkich fazach ich realizacji.
Zarządzanie konfiguracją umożliwia programistom śledzenie zmian. Z perspektywy tego
zarządzania system reprezentowany jest jako zbiór elementów konfiguracyjnych, z których
każdy może być kontrolowany i korygowany niezależnie od pozostałych. Ewolucja każdego
z tych elementów oznacza powstawanie kolejnych wersji systemu; utrzymywanie poszczegól-
nych wersji daje możliwość cofnięcia się do dobrze określonego stanu, w sytuacji gdy wprowa-
dzone do systemu zmiany okażą się niewłaściwe.
Zarządzanie konfiguracją daje także możliwość kontrolowania zmian. Gdy zdefinio-
wana zostanie pewna „linia bazowa", wszelkie zmiany muszą zostać przeanalizowane i zaapro-
bowane przed ich zaimplementowaniem. Menedżerowie zyskują dzięki temu pewność, że system
ewoluuje zgodnie z założonymi celami projektowymi i że liczba problemów, jakie mogą się
w związku z tym pojawić, pozostaje na niskim poziomie. Powrócimy do tej kwestii w roz-
dziale 13. „Zarządzanie konfiguracją".

1.5.4. Zarządzanie projektem


Zarządzanie projektem ma tę specyfikę, że nie manifestuje się samo przez się w postaci ja-
kiejś specyficznej aktywności. Ma raczej charakter nadzoru zapewniającego, że system ukoń-
czony zostanie w terminie i zgodnie z założonym budżetem, bez jakiegokolwiek uszczerbku
dla jakości. W praktyce nadzór taki polega na planowaniu harmonogramu i budżetu w połą-
czeniu z negocjacjami z klientem, zatrudnianiu programistów i organizowaniu ich w zespoły,
monitorowaniu statusu realizacji projektu i interweniowaniu w sytuacjach wyjątkowych.
Opis większości tych aktywności wykracza poza ramy tej książki, ograniczymy się zatem do
tych, które bezpośrednio odczuwalne są przez programistów, zwrócimy także uwagę na tech-
niki usprawniające komunikację między programistami a menedżerami. Tematykę tę znajdą
czytelnicy w rozdziale 14. „Zarządzanie projektem".

1.5.5. Cykl życiowy oprogramowania


W tym rozdziale opisujemy inżynierię oprogramowania w kategoriach aktywności modelo-
wania. Programiści budują modele dziedziny aplikacyjnej oraz dziedziny realizacyjnej, kon-
centrując się jedynie na szczegółach istotnych w danym kontekście i ignorując pozostałe — dzięki
temu mogą efektywniej rozwiązywać konkretne problemy i udzielać wiarygodnych odpowiedzi.
Jako że proces tworzenia systemu sam może być traktowany jako swoisty system, ze swymi
1.6. Analiza przypadku — system ARENA 57

specyficznymi danymi wejściowymi i wyjściowymi, aktywnościami i zasobami, przeto nie


powinien zaskakiwać fakt, iż może on być modelowany przy użyciu tych samych technik, które
charakterystyczne są dla inżynierii oprogramowania. Ogół modeli tego rodzaju, zwany cyklem ży-
ciowym oprogramowania, opiszemy z detalami w rozdziale 15. „Cykl życiowy oprogramowania".

1.5.6. Podsumowanie
Treść rozdziałów od 1. do 15. tej książki odzwierciedla bieżący stan wiedzy na temat metod
obiektowo zorientowanej inżynierii oprogramowania. Czytelnicy mogą potraktować tę treść
jako zawartość swoistej książki kucharskiej — problem jednak w tym, że książka kucharska
rzadko jest wystarczająca dla nowicjusza sztuki kulinarnej: nie zdoła on przygotować kom-
pletnego posiłku, polegając wyłącznie na książce kucharskiej. Gdy zabraknie mu któregoś
składnika i jest zmuszony improwizować, nie zda się ona na nic.
Dwa rozdziały: 14. „Zarządzanie konfiguracją", o treści skupiającej się na planowa-
niu i kontrolowaniu projektów, oraz 15. „Cykl życiowy oprogramowania" poświęcony
modelowaniu, usprawnianiu i powtarzalności procesów składających się na cykl życiowy
aplikacji, koncentrujące się na technikach i modelach, niosą optymistyczne raczej przesłanie
w kwestii realizowania projektów informatycznych. W rozdziale 16. „Wszystko razem, czy-
li metodologie" zajmujemy się jednak sytuacjami wymagającymi wyjścia poza tradycyjną
wiedze podręcznikową: opisujemy metodologie i heurystyki służące realizowaniu koncepcji
opisywanych w poprzednich rozdziałach, tyle że w specyficznych warunkach, przedstawiamy
kilka „zwinnych" i mniej zwinnych metodologii.

1.6. Analiza przypadku — system ARENA


Jednym z naszych założeń dotyczących tej książki jest stopniowanie trudności. W każdym
z rozdziałów wprowadzamy rozmaite koncepcje i opisujemy różnorodne aktywności, po-
siłkując się coraz to bardziej skomplikowanymi przykładami — od banalnych zadań, odpo-
wiednich na klasówkę, do prawdziwie złożonych problemów zaczerpniętych bądź to z różnych
kursów, bądź też z faktycznie realizowanych projektów. Ponadto aby umiejscowić opisywane
koncepcje w ogólnym kontekście inżynierii oprogramowania, posługujemy się obszerną anali-
zą przypadku, jakim jest system o nazwie ARENA.
ARENA to wielodostępny, webowy system służący do organizowania i przeprowadzania
rozgrywek. System ten jest niezależny od konkretnej gry w tym sensie, że organizator może
zaadaptować nową grę do standardów interfejsu ARENY, wysłać ją na serwer i natychmiast
zaanonsować jej obecność graczom i obserwatorom zlokalizowanym gdziekolwiek w internecie.
Organizatorzy mają możliwość definiowania nowych stylów rozgrywek, poprzez określenie
szczegółowych zasad wygranej oraz przegranej i sporządzania rankingów zgodnie z tymi
zasadami. By zrekompensować sobie koszty operacyjne, organizatorzy mają ponadto możli-
wość umieszczania w scenerii gry banerów zawierających reklamy sponsorów.
W kończących każdy z rozdziałów sekcjach zatytułowanych „ARENA — analiza przy-
padku" dyskutujemy rozmaite kwestie związane z systemem ARENA, rozważamy różnorodne de-
cyzje projektowe i kompromisy wynikające z treści poszczególnych rozdziałów, a także na-
wiązujemy do końcowych sekcji poprzednich rozdziałów, uwydatniając w ten sposób fakt
wzajemnych powiązań między nimi.
58 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

• I tak na przykład w rozdziale 4. „Zbieranie wymagań" pokazujemy zaczątki two-


rzenia zbioru przypadków użycia na podstawie informacji dostarczanej przez
klienta: definiujemy sposób organizowania i rozgrywania turniejów oraz zasady
uczestnictwa graczy w tych turniejach. Zadając klientowi coraz więcej pytań, po-
zbywamy się kolejnych niejasności i odkrywamy luki w koncepcji systemu.
• W rozdziale 5. „Analiza wymagań" opisujemy konstruowanie modelu obiektowego
i modelu zachowania na podstawie modelu przypadku użycia. Pokazujemy także,
jak tworzenie owych modeli prowadzi do ulepszania modelu przypadku użycia
i odkrywania dodatkowych wymagań — definiujemy na przykład w sposób bardziej
formalny koncepcję wyłącznego sponsoringu i sposób decydowania o sponso-
rowaniu turnieju; demonstrujemy także konsolidowanie modelu obiektowego.
• W rozdziale 7. „Projekt systemu: realizacja celów projektowych" dokonujemy wyboru
architektury klient-serwer i frameworku dla realizacji systemu, ustalamy także
sposób przechowywania danych i kontroli dostępu. Omawiamy różnorodne me-
chanizmy uwierzytelniania użytkowników w internecie, identyfikujemy obiekty
wymagające przechowywania w sposób trwały (stan gry, wyniki rozgrywek, profile
graczy i tym podobne) i wreszcie przeprowadzamy dekompozycję systemu ARENA
na prostsze podsystemy, z których każdy staje się możliwy do ogarnięcia przez poje-
dynczego programistę.
• W rozdziale 8. „Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań
wzorcowych" rozpoznajemy dodatkowe obiekty dziedziny realizacyjnej, umożliwia-
jące wypełnienie luki między projektem systemu a jego implementacją. Wykorzy-
stujemy wielokrotnie te same rozwiązania szablonowe, poprzez wybór wzorców
projektowych stosownych dla specyficznych problemów — przykładowo wzorzec
„strategia" wykorzystywany jest do enkapsulacji różnych stylów rozgrywek.
• Rozdział 10. „Odwzorowywanie modelu na kod" poświęcony jest translacji zbu-
dowanych modeli UML na kod w języku Java oraz analizowaniu projektu obiektów
pod kątem tkwiących w nich możliwości optymalizacyjnych. Tym samym roz-
dział ten jest ilustracją ścisłego związku iteracyjnego między projektem obiektów
a implementacją.

Produkty związane z systemem ARENA, wraz z jego wersją demonstracyjną, dostępne


są pod adresem http://wwwbruegge.in.tum.de/OOSE/WebHome.

1.7. Literatura uzupełniająca


Podstawowe, fundamentalne problemy związane z inżynierią oprogramowania nie są wyna-
lazkiem dnia dzisiejszego — bogate ślady ich obecności znaleźć można z łatwością w literaturze
ostatnich dziesięcioleci.
I tak w swojej książce [Brooks, 1995], wydanej po raz pierwszy w roku 1975, F. Brooks
snuje refleksje na temat własnych doświadczeń związanych z konstruowaniem systemu ope-
racyjnego dla komputera mainframe IBM/360. Projekt wyceniony na miliony dolarów, za-
planowany na kilka lat, nie został zrealizowany w terminie i nie zmieścił się w zakładanym
1.8. Ćwiczenia 59

budżecie. Od tego czasu minęło wiele lat, programiści dostali do swych rąk nowe technologie,
narzędzia i metody — po to, by dzielniej zmagać się z kolejnymi problemami, coraz bardziej
kosztownymi coraz bardziej spektakularnymi. Wiele podstawowych lekcji wyniesionych z do-
świadczeń minionych dekad wciąż zachowuje swą aktualność.
Książka P. Neumanna [Neumann, 1995] zawiera ciekawy przegląd rozmaitych nieszczęść
spowodowanych (ogólnie rzecz ujmując) wadami technologii informatycznych. Autor, uświa-
damiając czytelnikom powagę konsekwencji, jakie nieść może ze sobą ułomność procesu
wytwarzania oprogramowania, rozważa jednocześnie przyczyny opisywanych niepowodzeń
i dostarcza wskazówki pomagające unikać takich i podobnych incydentów. Tę naprawdę rze-
czową książkę przeczytać powinien każdy programista, któremu choćby tylko marzy się two-
rzenie skomplikowanych systemów informatycznych.
Książka K. Poppera [Popper, 1992] to esej na temat konstruowania wiedzy. Autor zrywa
z tradycyjnymi teoriami w tym względzie, datującymi się jeszcze z czasów Arystotelesa, przed-
stawiając własny punkt widzenia, zgodnie z którym wiedza naukowa, od kiedy tylko zaistniała
jako pojęcie, rozwija się jako odrębna całość poprzez selekcję. Jako że tworzenie oprogramo-
wania jest procesem zespołowym, związanym ze zdobywaniem wiedzy i konstrukcją, wspo-
mniana książka może być użyteczna jako zachęcająca do krytycznych przemyśleń i cechująca
się niecodziennym spojrzeniem na inżynierię oprogramowania.
W tej książce skupiamy się na obiektowo zorientowanej inżynierii oprogramowania i ad-
resujemy ją do zaawansowanych kursów o tej tematyce. W konsekwencji pomijamy więc wiele
zagadnień o charakterze historycznym lub czysto menedżerskim, takich jak metryki opro-
gramowania, szacowanie kosztów czy metody formalne konstruowania programów. Prze-
gląd tych zagadnień zainteresowani czytelnicy znajdą w podręcznikach o bardziej ogólnym
charakterze, takich jak na przykład autorstwa I. Sommerville'a [Sommerville, 2006] czy
R. S. Pressmana [Pressman, 2009].

1.8. Ćwiczenia
1.1. Co jest celem modelowania?
1.2. Język programowania to notacja przeznaczona do reprezentowania algorytmów
i struktur danych. Wymień dwie zalety i dwie wady użytkowania tej notacji jako
jedynej w procesie tworzenia aplikacji.
1.3. Wyobraź sobie, że stajesz w obliczu realizacji zadania, na temat którego masz nikłe
pojęcie — na przykład przed skonstruowaniem samochodu w ogóle nieprodukują-
cego spalin. Jak zabrałbyś się do takiego zadania?
1.4. Jak rozumiesz stwierdzenie „pozyskiwanie wiedzy nie jest procesem sekwencyjnym"?
Podaj konkretny przykład uzasadniający prawdziwość tego stwierdzenia.
1.5. Spróbuj wymyślić hipotetyczną racjonalizację dla następujących decyzji projektowych:
• Automat realizujący system Ti cketDi s t r i butor nie może być wyższy niż półtora
metra.
• System Ti cketDi s t r i butor powinien być zrealizowany w oparciu o dwa dublujące
się komputery.
60 Rozdział 1. • Wprowadzenie do inżynierii oprogramowania

• Automat realizujący system Ti cketDi s t r i butor powinien komunikować się


z użytkownikiem za pomocą ekranu dotykowego, wyświetlającego informacje
i umożliwiającego wprowadzanie poleceń, przy czym w każdej chwili powinno być
możliwe anulowanie rozpoczętej transakcji przez naciśnięcie jednego przycisku.
1.6. Które z poniższych wymagań mają charakter funkcyjny, a które pozafunkcyjny?
• System TicketDistributor powinien umożliwiać użytkownikowi zakup biletu
tygodniowego.
• Kod systemu Ti cketDi s t r i butor powinien być napisany w języku Java.
• System Ti cketDi s t r i butor musi być łatwy w użytkowaniu.
• System T i c k e t D i s t r i b u t o r musi być dostępny bez przerwy.
• System Ti cketDi s t r i butor powinien wskazywać użytkownikowi numer telefonu,
pod którym może on uzyskać niezbędną pomoc w przypadku awarii i innych
nieoczekiwanych zdarzeń.
1.7. Jak myślisz, które z poniższych decyzji podjęte zostały w fazie zbierania wymagań,
a które na etapie projektowania systemu:
• System TicketDistributor składa się z następujących podsystemów: interfejsu
użytkownika, obliczania ceny zgodnie z taryfą i komunikacji z centralnym
komputerem.
• Platforma sprzętowa systemu T i c k e t D i s t r i b u t o r oparta jest na procesorze
PowerPC.
• System Ti cketDi s t r i butor oferuje użytkownikowi system pomocy online.
1.8. W których miejscach poniższego opisu termin „konto" odnosi się do dziedziny
aplikacyjnej, a w których do dziedziny realizacyjnej?
„Opracowywany jest system obsługi kont bankowych dla mobilnych klientów. Jed-
nym z podstawowych problemów tego zadania jest zapewnienie użytkownikowi do-
stępu do konta nawet wówczas, gdy nie może on nawiązać połączenia z serwerem.
Jedna z propozycji zakłada dostęp do konta poprzez mobilny komputer nawet
wówczas, gdy serwer jest niedostępny lub nieczynny — w takiej sytuacji lokalne konto
we wspomnianym komputerze zawierać będzie informację aktualną w momencie
ostatniego połączenia z serwerem".
1.9. Jaka jest różnica między zadaniem a aktywnością?
1.10. Samolot pasażerski składa się z wielu milionów komponentów i produkowany jest
z udziałem tysięcy ludzi różnych specjalności. Podobnie złożonym przedsięwzięciem
jest budowa mostu, przez który biegnie czteropasmowa autostrada. Pierwsza wersja
MS Word dla Windows, udostępniona w 1989 roku — cztery lata po zakładanym
terminie — zrealizowana została kosztem 55 roboczolat w postaci aplikacji zawierającej
249 000 wierszy kodu źródłowego. Samoloty i mosty realizowane są zazwyczaj w termi-
nie i zgodnie z budżetem, czego przeważnie nie można powiedzieć o projektach pro-
gramistycznych. Gdzie Twoim zdaniem leży przyczyna tej różnicy?
Bibliografia 61

Bibliografia
[Babich, 1986] W. Babich Software Configuration Management, Addison-Wesley, Reading,
MA, 1986.
[Booch, 1994] G. Booch Object-Oriented Analysis and Design with Applications, wyd. drugie,
Benjamin/Cummings, Redwood City, CA, 1994.
[Brooks, 1995] F. P. Brooks The Mythical Man Month: Anniversary Edition: Essays on Software
Engineering, wyd. Addison-Wesley, Reading, MA, 1995.
[Bruegge, 1992] B. Bruegge „Teaching an industry-oriented software engineering course",
Software Engineering Education, SEI Conference, Lecture Notes in Computer
Sciences, t. 640, str. 65 - 87, Springer-Verlag, październik 1992.
[Bruegge i Coyne, 1993] B. Bruegge, R. Coyne „Model-based software engineering in larger scale
project courses", IFIP Transactions on Computer Science and Technology,
t. A-40, str. 273 - 287, Elsevier Science, Netherlands, 1993.
[Bruegge i Coyne, 1994] B. Bruegge, R. Coyne „Teaching iterative object-oriented development:
Lessons and directions", w: J. L. Diaz-Herrera (red.), 7h Conference on
Software Engineering Education, Lecture Notes in C o m p u t e r Science,
t. 750, str. 413 - 427, Springer-Verlag, 1994.
[Coyne i in., 1995] R. Coyne, B. Bruegge, A. Dutoit, D. Rothenberger „Teaching m o r e
c o m p r e h e n s i v e model-based software engineering: Experience with
Objectory's use case approach", w: L. Ibraham (red.), 8'k Conference on
Software Engineering Education, Lecture Notes in C o m p u t e r Science,
str. 339 - 374, Springer-Verlag, kwiecień 1995.
[De Marco, 1978] T. De M a r c o Structured Analysis and System Specification, Yourdon,
N e w York, 1978.
[D'Souza i Wills, 1999] D. F. D'Souza, A. C. Wills Objects, Components, and Frameworks with UML:
The Catalysis Approach, Addison-Wesley, Reading, MA, 1999.
[IEEE Std. 610.12-1990] IEEE Standard Computer Dictionary: A Compilation of IEEE Standard
ComputerGlossaries, NY, 1990.
[Jacobson i in. 1992] I. Jacobson, M. Christerson, P. Jonsson, G. Overgaard Object-Oriented Software
Engineering — A Use Case Driven Approach, Addison-Wesley, Reading, MA, 1992.
[Moran i Carroll, 1996] T. P. Moran i J. M. Carroll (red.) Design Rationale: Concepts, Techniques,
and Use, Lawrence Erlbaum Associates, Mahwah, NJ, 1996.
[Neumann, 1995] P. G. Neumann Computer-Related Risks, Addison-Wesley, Reading, MA, 1995.
[OMG, 2009] Object Management Group, OMG Unified Modeling Language Specification.
Version 2.2, 2009. http://www.omg.org.
[Popper, 1992] K. P o p p e r Objective Knowledge: An Evolutionary Approach, Clarendon,
O x f o r d , 1992.
[Pressman, 2009] R S. Pressman Software Engineering: A Practitioner's Approach, wyd. siódme,
McGraw-Hill, 2009.
[Royce, 1998] W . Royce Software Project Management: A Unified Framework,
Addison-Wesley, Reading, MA, 1998.
[Rumbaugh i in., 1991] J. Rumbaugh, M. Błaha, W. Premerlani, F. Eddy, W. Lorensen Object-Oriented-
Modeling and Design, Prentice Hall, Englewood Cliffs, NJ, 1991.
[Simon, 1970] H. A. Simon The Sciences of the Artificial, MIT Press, Cambridge, MA, 1970.
[Sommerville, 2006] I. Sommerville Software Engineering, wyd. ósme Addison-Wesley,
Reading, MA, 2006.
[Spivey, 1989] J. M. Spivey The Z Notation, A Reference Manual, Prentice Hall, Hertfordshire,
2.1. Wprowadzenie 64

2.2. Ogólnie o UML 65


2.2.1. Diagramy przypadków użycia 65
2.2.2. Diagramy klas 65
2.2.3. Diagramy interakcji 67
2.2.4. Diagram stanów 67
2.2.5. Diagramy aktywności 68

2.3. Podstawowe koncepcje modelowania 69


2.3.1. Systemy, modele i widoki 69
2.3.2. Typy danych, abstrakcyjne typy danych i instancje 72
2.3.3. Klasy, klasy abstrakcyjne i obiekty 73
2.3.4. Klasy zdarzeniowe, zdarzenia i komunikaty 75
2.3.5. Modelowanie zorientowane obiektowo 76
2.3.6. Falsyfikacja i prototypowanie 77

2.4. UML — głębszy wgląd 78


2.4.1. Diagramy przypadków użycia 79
2.4.2. Diagramy klas 86
2.4.3. Diagramy interakcji 95
2.4.4. Diagramy stanów 98
2.4.5. Diagramy aktywności 101
2.4.6. Organizacja diagramów 104
2.4.7. Rozszerzenia diagramów 106

2.5. Literatura uzupełniająca 107

2.6. Ćwiczenia 108

Bibliografia 109
Modelowanie
m
w języku UML

Każdy mechanik to zna: nie możesz kupić osobno jakiejś części,


bo producent potraktował ją jako element większej całości.
— Robert Pirsig Zen i sztuka oporządzania motocykla

X - J ż y w a j ą c właściwej notacji, możemy nawet skomplikowane idee wyrażać w sposób zwięzły


i precyzyjny. W sytuacji, gdy w realizacji projektu uczestniczy wielu ludzi, o odmiennych pro-
filach kulturowych i różnym profilu wyedukowania technicznego, dokładność i klarowność to
cechy niezbędne — bez nich wzajemna komunikacja błyskawicznie pogrąża się w chaosie.
Aby notacja mogła czynić komunikację dokładną i czytelną, powinna cechować się
przede wszystkim dobrze zdefiniowaną semantyką oraz musi być dobrze przystosowana do
reprezentowania określonych aspektów systemu. Musi być ponadto dobrze zrozumiała dla
wszystkich uczestników — i tu ujawnia się znaczenie przyjęcia odpowiednich standardów
i konwencji: nawet w przypadku licznego zespołu, gdy wszyscy posługują się jednolitymi
standardami, wydatnie zmniejsza się okazja do różnych nieporozumień, fałszywych inter-
pretacji czy dwuznaczności. I vice versa: gdy w obiegu funkcjonują różne dialekty albo notacja
jest zbyt specjalistyczna, może się zdarzyć, że poszczególni uczestnicy przyjmują własne, róż-
niące się interpretacje tych samych faktów, co porozumienia bynajmniej nie ułatwia.
Na potrzeby tej książki wybraliśmy notację w postaci języka UML (Universal Modeling
Language — uniwersalny język modelowania), zdefiniowanego w OMG Unified Modeling
Language Superstructure. Version 2.2 z 2009 roku [OMG, 2009], ponieważ dostarcza on sze-
rokiego spektrum środków do reprezentowania różnorodnych aspektów systemu i stał się
zaakceptowanym standardem notacyjnych w środowisku tworzenia oprogramowania na skalę
przemysłową. W tym rozdziale opiszemy ogólne koncepcje modelowania, ze szczególnym
uwzględnieniem modelowania zorientowanego obiektowo. Omówimy pięć fundamentalnych
rodzajów notacji UML, których używać będziemy w całej książce; są to diagramy przypad-
ków użycia, diagramy klas, diagramy interakcji, diagramy stanów i diagramy aktywności.
Dla każdej z tych kategorii wyjaśnimy podstawową semantykę i zilustrujemy ją konkretnymi
przykładami. Do każdej z nich będziemy — oczywiście — wielokrotnie powracać w książce
przy okazji opisywania różnych rodzajów aktywności. W dwóch rozdziałach czytelnicy znajdą
także notację innego typu: w rozdziale 6. „Projektowanie systemu — dekompozycja na pod-
systemy" wykorzystamy diagramy wdrożenia UML, natomiast w rozdziale 14. „Zarządzanie
projektem" posłużymy się notacją PERT.
64 Rozdział 2. • Modelowanie w języku UML

2.1. Wprowadzenie
Język UML powstał jako efekt unifikacji kilku innych notacji: OMT (Object Modeling Technique
[Rumbaugh i in., 1991]), notacji Boocha [Booch, 1994] oraz OOSE (Object-Oriented Software
Engineering [Jacobson i in., 1992]). Pewien wpływ na ostateczną postać języka wywarło także
kilka innych standardów, takich jak notacje zaproponowane przez Mellora i Shlaer [Mellor
i Shlaer, 1998], Coada i Yourdona [Coad i in., 1995], Wirfs-Brock [Wirfs-Brock i in., 1990]
oraz Martina i Odella [Martin & Odell, 1992],
Celem języka UML jest dostarczenie standardowej notacji, która z jednej strony byłaby
użyteczna dla wszelkich metodologii zorientowanych obiektowo, z drugiej natomiast pro-
mowała najlepsze cechy swych poprzedniczek. Istotnie, w języku UML spotkać możemy dia-
gramy przypadków użycia przejęte z OOSE oraz wiele cech wywodzących się z OMT diagramów
klas. Jednocześnie UML przynosi wiele nowatorskich, niespotykanych wcześniej koncepcji,
takich jak mechanizmy rozszerzające czy język ograniczeń. UML zaprojektowany został z myślą
o szerokim zakresie zastosowań, dostarcza zatem konstrukcji przydatnej dla systemów i ak-
tywności różnego rodzaju (na przykład systemów rozproszonych, analizy systemów, pro-
jektowania systemu czy wdrażania). Tworzenie systemu ujmowane jest z perspektywy UML
w postaci trzech różnych modeli (patrz rysunek 1.2). Oto one.

• Model funkcjonalny, reprezentowany na gruncie UML przez diagramy przypad-


ków użycia, postrzegający system z perspektywy jego pojedynczego użytkownika.
• Model obiektowy, reprezentowany w postaci diagramów klas i opisujący strukturę
systemu w kategoriach obiektów, atrybutów, skojarzeń i operacji. Na etapie zbierania
wymagań i analizy model obiektowy przyjmuje swą postać początkową — analitycz-
nego modelu obiektowego — i opisuje różne koncepcje zastosowania systemu. W fazie
projektowania systemu model ten rozwijany jest do postaci obiektowego modelu pro-
jektu systemu i wzbogacony zostaje między innymi o opis interfejsów poszczególnych
podsystemów. Kolejne stadium przeobrażeń modelu obiektowego to model projektu
obiektów, zawierający szczegółowy opis poszczególnych obiektów składających się na
dziedzinę realizacyjną.
• Model dynamiczny, obecny w UML w postaci diagramów interakcji, diagramów
stanów i diagramów aktywności, opisujących wewnętrzne aspekty zachowania sys-
temu. Diagramy interakcji opisują zachowanie systemu jako sekwencję komunikatów
wymienianych w ramach zbioru obiektów, podczas gdy diagramy stanów koncentrują
się na zmianie stanu pojedynczych obiektów i możliwych przejściach między poszcze-
gólnymi stanami. Z kolei diagramy aktywności opisują zachowanie systemu w kate-
goriach przepływu sterowania i przepływu danych.

W tym rozdziale opiszemy diagramy UML przeznaczone do reprezentowania każdego


z tych modeli. Ich prezentacja wiązać się będzie mimowolnie z pewnym wyzwaniem: otóż
zrozumienie przeznaczenia danej notacji wymaga pewnej znajomości aktywności, na potrzeby
których została stworzona, a przecież nie sposób jednoznacznie i precyzyjnie opisać tych ak-
tywności bez zrozumienia służącej do tego notacji! Aby poradzić sobie z tą kłopotliwą rekuren-
cją, przedstawiać będziemy poszczególne koncepcje języka UML w sposób iteracyjny — i tak
2.2. Ogólnie o UML 65

w sekcji 2.3 opiszemy fundamentalne koncepcje modelowania, w sekcji 2.4 przyjrzymy się
dokładniej pięciu wymienionym rodzajom diagramów w świetle tychże koncepcji, zaś w kolej-
nych rozdziałach omawiać będziemy owe diagramy na rozmaitym poziomie szczegółowości,
przy okazji dyskusji na temat odzwierciedlanych przez nie aktywności.

2.2. Ogólnie o UML


W tej sekcji opiszemy krótko pięć podstawowych rodzajów notacji UML:

• diagramy przypadków użycia (patrz sekcja 2.2.1),


• diagramy klas (patrz sekcja 2.2.2),
• diagramy interakcji (patrz sekcja 2.2.3),
• diagramy stanów (patrz sekcja 2.2.4),
• diagramy aktywności (patrz sekcja 2.2.5).

2.2.1. Diagramy przypadków użycia


Diagramy przypadków użycia wykorzystywane są w fazie zbierania wymagań do reprezento-
wania elementów funkcjonalnych systemu. Koncentrują się na zachowaniu systemu postrze-
ganym z zewnątrz. Przypadek użycia opisuje funkcję realizowaną przez system jako efekt
widoczny dla aktora, z kolei aktor stanowi personifikację encji uwikłanej w interakcję z przed-
miotowym systemem — taką encją może być fizyczne otoczenie tego ostatniego, użytkownik
lub inny system. Efektem identyfikacji aktora i przypadku życia jest zdefiniowanie warunków
brzegowych (granicznych) systemu, czyli wyraźne określenie granicy oddzielającej zadania
realizowane przez system od zadań realizowanych przez jego środowisko. Granica ta ma
kształt figury zamkniętej: aktorzy znajdują się na zewnątrz niej, wewnątrz rozgrywają się
przypadki użycia.
Na rysunku 2.1 widoczny jest diagram przypadków użycia związanych z prostym ze-
garkiem elektronicznym. Aktor WatchUser może używać swojego zegarka do sprawdzenia
aktualnego czasu (czemu odpowiada przypadek użycia ReadTime) bądź do skorygowania usta-
wienia czasu (ta czynności reprezentowana jest przez przypadek testowy SetTime). Wymianę
baterii (reprezentowaną przez przypadek użycia ChangeBattery) woli jednak powierzyć ze-
garmistrzowi, którego na diagramie reprezentuje aktor WatchRepai rPerson.

2.2.2. Diagramy klas


Przeznaczeniem diagramów klas jest opisywanie struktury systemu. Klasy są abstrakcjami
określającymi wspólną strukturę i wspólne zachowanie określonego zbioru obiektów. Obiekty
są instancjami (egzemplarzami) klas; w czasie funkcjonowania systemu instancje te mogą
być tworzone, modyfikowane i niszczone. Dla każdego obiektu określony jest stan, na który
składają się wartości jego atrybutów i jego połączenia z innymi obiektami.
Diagram klas jest opisem systemu w kategoriach obiektów, klas, atrybutów, operacji
i skojarzeń między obiektami. Przykładowy diagram z rysunku 2.2 to diagram klas uwidocz-
niający elementy każdego zegarka reprezentowanego przez klasę Simpl eWatch. Obiekt tej klasy
66 Rozdział 2. • Modelowanie w języku UML

Rysunek 2.1. Diagram przypadków użycia ukazujący funkcjonalność prostego zegarka Simpl eWatch.
Aktor WatchUser może bądź to odczytywać wskazanie czasu (przypadek użycia ReadTime), bądź je
korygować (przypadek użycia SetTime). Jednak tylko aktor WatchRepai rPerson może wymieniać baterię
w zegarku (przypadek użycia ChangeBattery). Aktorzy reprezentowani są na diagramie przez sugestywne
figurki, zaś przypadki użycia — przez elipsy. Prostokąt otaczający elipsy jest granicą między aktorami
a przypadkami użycia

Rysunek 2.2. Diagram klas przedstawiający elementy prostego zegarka

skojarzony jest z obiektami klas PushButton, Display, Time i Battery reprezentującymi od-
powiednio przyciski, wyświetlacze, czas i baterie. Liczby przy końcach linii oznaczających
skojarzenia ukazują liczbę skojarzonych obiektów — i tak obiekt klasy Simpl eWatch skoja-
rzony jest z dokładnie dwoma obiektami klasy PushButton (zegarek ma dwa przyciski), jed-
nym obiektem klasy Di spl ay (zegarek jest prosty, ma więc jeden wyświetlacz) i jednym obiek-
tem klasy Time (każdy zegarek, jakkolwiek byłby nastawiony, wskazuje w danej chwili jakiś
konkretny czas), i dwoma obiektami klasy Battery (dwie bateryjki służą do zasilania zegarka).
Podobnie każdy obiekt klas PushButton, Di spl ay, Time i Battery skojarzony jest z dokładnie
jednym obiektem klasy Simpl eWatch (zakładamy, że bateria znajduje się wewnątrz zegarka
i wskazuje on jakiś czas).
Na etapie analizy skojarzenia z diagramu reprezentują istniejące w rzeczywistości związki
między encjami — i tak na przykład zegarek (Simpl eWatch) wyposażony jest w odpowiednią
liczbę przycisków (PushButton), posiada wyświetlacz (Di spl ay), wskazuje konkretny czas
(Time) i zasilany jest energią pochodzącą z odpowiedniej liczby bateryjek (Battery). W tym
przykładzie skojarzenia są symetryczne: zegarek nie mógłby funkcjonować bez przycisków, zaś
przycisk nie ma znaczenia samoistnego, a istnieje jedynie jako część zegarka. UML umożliwia
także reprezentowanie relacji jednokierunkowych, czego przykłady przedstawimy w sekcji
2.4.2. Na etapie implementacji skojarzenia odzwierciedlane są jako referencje lub wskaźniki
(pointers) do obiektów.
2.2. Ogólnie o UML 67

2.2.3. Diagramy interakcji


Zadaniem diagramów interakcji jest formalizacja dynamicznego zachowania się systemu
i uwidocznienie komunikacji między obiektami. Okazuje się to użyteczne dla identyfikowania
dodatkowych obiektów związanych z przypadkami użycia — każdy obiekt mający związek
z konkretnym przypadkiem użycia nazywać będziemy obiektem uczestniczącym. Diagram
interakcji reprezentuje związki między obiektami uczestniczącymi — na rysunku 2.3 widzimy
szczególny przypadek diagramu interakcji, zwany diagramem sekwencji, dla przypadku
użycia SetTime naszego zegarka Simpl eWatch. Skrajna lewa kolumna reprezentuje aktora
WatchUser inicjującego przypadek użycia — etykietowane strzałki reprezentują wykonywane
przez niego czynności w stosunku do zegarka. W tym przykładzie WatchUser naciska dwu-
krotnie przycisk 1 i jednokrotnie przycisk 2, by skorygować wskazanie czasu o jedną minutę
do przodu. Przypadek użycia SetTime kończy się, gdy WatchUser naciśnie równocześnie oba
przyciski.

Rysunek 2.3. Diagram sekwencji dla zegarka (: Watch). Skrajna lewa kolumna reprezentuje moment,
w którym aktor : WatchUser inicjuje przypadek użycia. Pozostałe kolumny związane są z obiektami
uczestniczącymi w przypadku użycia. Podkreślenie nazwy obiektu oznacza, że mamy do czynienia z kon-
kretnym obiektem, nie z całą klasą. Etykietowane strzałki reprezentują sygnały (bodźce), które aktor
lub inny obiekt wysyła do innych obiektów

2.2.4. Diagram stanów


Diagram stanów opisuje zachowanie się pojedynczego obiektu, rozumiane jako przebywanie
w danej chwili w określonym stanie oraz przechodzenie z jednego stanu do innego. Pod pojęciem
„stanu" rozumiemy tu zbiór wartości związanych z obiektem, zaś jako „przejście" — zmianę
bieżącego stanu na inny, pod wpływem spełnienia określonego warunku. Na rysunku 2.4 przed-
stawiliśmy diagram stanów dla zegarka Watch. Zaczernione kółeczko reprezentuje wyróżniony
stan początkowy — BI i nkHours, zaś takie samo kółeczko otoczone białym pierścieniem — stan
końcowy (StopBl inking).
68 Rozdział 2. • Modelowanie w języku UML

Rysunek 2.4. Diagram stanów dla przypadku użycia SetTime zegarka Watch

Zauważmy, że diagram stanów reprezentuje informację różną od wynikającej z diagramu


sekwencji na rysunku 2.3: diagram sekwencji uwidocznia komunikaty wymieniane między
obiektami jako efekt zewnętrznych zdarzeń wywoływanych przez aktora, zaś diagram stanów
koncentruje się na zmianie stanów konkretnego obiektu w rezultacie tychże zdarzeń.

2.2.5. Diagramy aktywności


Diagram aktywności odzwierciedla zachowanie systemu w kategoriach aktywności. Ak-
tywności jako elementy modelowania reprezentują wykonywanie zbioru lub ciągu operacji
— konkretna aktywność może być zapoczątkowana w konsekwencji zakończenia innej aktyw-
ności, udostępnienia jakiegoś obiektu czy też ogólnie wskutek wystąpienia jakiegoś zdarzenia
zewnętrznego. Programiści natychmiast rozpoznają tu podobieństwo do schematów blokowych,
odzwierciedlających przepływ sterowania (następstwo wykonywanych instrukcji i procedur)
lub przepływ danych (czyli wpływ wykonywanych operacji na poszczególne obiekty). W dia-
gramie widocznym na rysunku 2.5 uwidocznione są aktywności związane z zarządzaniem jakaś
sytuacją nadzwyczajną, określoną ogólnie jako „Incydent". Zaokrąglone prostokąty repre-
zentują aktywności, strzałki między nimi — przepływ sterowania; pionowe belki są punkta-
mi synchronizacji przepływu zdarzeń: każda z aktywności AliocateResources („przydziel
zasoby"), CoordinateResources („koordynuj wykorzystywanie zasobów") i Documentlncident
(„udokumentuj incydent") może zostać zainicjowana dopiero po tym, jak zakończy się aktyw-
ność Open Incident („zareaguj na incydent"). Nie ma natomiast żadnych przeciwwskazań, by
aktywności Al 1 ocateResources, CoordinateResources i Documentlncident realizowane były
równolegle, niezależnie od siebie. Wszystkie one muszą się jednak zakończyć, by mogła roz-
począć się aktywność Archi velncident („archiwizuj incydent").
2.3. Podstawowe koncepcje modelowania 69

Rysunek 2.5. Przykład diagramu aktywności, reprezentującego zachowanie systemu w kategoriach ak-
tywności i ich warunków wstępnych: zakończenie jednej aktywności może stanowić wyzwalacz inicjujący
inną aktywność (Openlncident — reakcja na zdarzenie, „otwarcie" incydentu; Al locate Resources
— przydzielenie zasobów niezbędnych do obsługi incydentu; Coordinate Resources — koordynacja
wykorzystywania zasobów; Document Incident — dokumentowanie incydentu; Archive Incident
— przeniesienie dokumentacji incydentu do archiwum)

Tak z grubsza prezentują się diagramy pięciu kategorii, składające się na podstawową
notację UML. W następnych sekcjach przyjrzymy się im bardziej szczegółowo: w sekcji 2.3
omówimy podstawowe koncepcje modelowania — pojęcia systemu, modelu, typu, instancji,
abstrakcji i falsyfikacji. W sekcjach 2.4.1 - 2.4.5 opiszemy szczegółowo diagramy przypadków
użycia, diagramy klas, diagramy sekwencji, diagramy stanów i diagramy aktywności, ilustrując
ich zastosowania prostymi przykładami. Sekcję 2.4.6 poświęcimy różnorodnym konstrukcjom
pokrewnym, wykorzystywanym we wszystkich pięciu typach diagramów — między innymi
pakiety i notatki. Notację tę stosować będziemy w całej książce w celu opisywania systemów
informatycznych, produktów, aktywności i organizacji projektów. Wykorzystując systema-
tycznie, w sposób spójny, niewielkie podzbiory tej notacji, mamy nadzieję dostarczyć czy-
telnikowi obszerną wiedzę na temat możliwości języka UML.

2.3. Podstawowe koncepcje modelowania


W tej sekcji opiszemy podstawowe koncepcje modelowania. Rozpoczniemy od zdefinio-
wania pojęć system, model i widok oraz omówimy podstawowe zadania modelowania.
Wyjaśnimy ich związek z językami programowania, a dokładniej — z takimi ich elementami
jak typy danych, klasy, instancje i obiekty. Na koniec pokażemy, jak obiektowo zorientowane
modelowanie skupia się na budowaniu abstrakcji środowiska systemu, zmierzając do stwo-
rzenia modelu systemu.

2.3.1. Systemy, modele i widoki


System to zorganizowany zbiór komunikujących się części. Interesować nas będą systemy
inżynieryjne, czyli skonstruowane z określonym przeznaczeniem — w przeciwieństwie do
systemów naturalnych, takich jak Układ Słoneczny, których cel istnienia na zawsze pozostanie
dla nas tajemnicą. I tak na przykład samochód, składający się z czterech kół, podwozia, silnika
i karoserii, zaprojektowany został z myślą o przewożeniu osób. Zegarek, złożony z baterii,
obwodu scalonego, przycisków i wyświetlacza, służy do pomiaru upływającego czasu. System
70 Rozdział 2. • Modelowanie w języku UML

płacowy, obejmujący komputer mainframe, drukarki, dyski, oprogramowanie i personel, ma


za zadanie obliczanie i wypłatę wynagrodzeń dla pracowników firmy. Poszczególne części
systemu mogą być same z siebie rozpatrywane jako prostsze systemy i z tej racji nazywane
podsystemami — silnik samochodu, składający się między innymi z cylindrów, zaworów
i modułu wtrysku paliwa, jest przykładem takiego podsystemu, podobnie jak układ scalony
zegarka czy komputer realizujący obliczenia w ramach systemu płacowego. Dekompozycję tę
można — oczywiście — stosować rekurencyjnie, aż do otrzymania części na tyle prostych, że
ich zrozumienie nie wymaga już dalszej dekompozycji.
Wiele systemów składa się dużej liczby podsystemów połączonych ze sobą skompli-
kowanymi zależnościami — często na tyle skomplikowanymi, iż niemożliwe jest pełne zro-
zumienie systemu przez pojedynczego człowieka. Modelowanie stanowi skuteczny sposób
radzenia sobie z tą złożonością. Złożony system opisywany jest zwykle przez wiele modeli,
z których każdy skupia się na określonym jego aspekcie lub określonym stopniu szczegółowo-
ści jego postrzegania. Modelowanie to konstruowanie abstrakcji systemu, skupiającej się jedy-
nie na szczegółach istotnych w danym kontekście i ignorującej kwestie nieistotne z jego per-
spektywy. Odróżnienie jednych od drugich jest każdorazowo zadaniem specyficznym dla
konkretnego zastosowania.
Załóżmy, że chcemy zbudować samolot. Nawet przy udziale licznych ekspertów z róż-
nych dziedzin nie jesteśmy w stanie zbudować go od zera i spodziewać się, że z powodzeniem
odbędzie swój dziewiczy lot. Musimy w zamian rozpocząć od zbudowania modelu w małej
skali i przetestowania jego własności fizycznych w tunelu aerodynamicznym. W czasie tych
testów interesująca będzie dla nas jedynie sylwetka samolotu, w odróżnieniu od takich
nieistotnych szczegółów jak pulpit w kabinie pilotów czy silniki. Gdy chcemy wyszkolić
pilotów na potrzeby tego samolotu, musimy skonstruować odpowiedni symulator — ten
z kolei odzwierciedlać będzie rozkład i funkcje poszczególnych narzędzi sterujących samolotu,
bez jakiegokolwiek związku z jego zewnętrzną sylwetką. Zarówno małoskalowy model samo-
lotu, jak i symulator lotów są znacznie mniej skomplikowane od całości samolotu, którego
reprezentację stanowią. Modelowanie umożliwia zmaganie się ze złożonością na zasadzie
„dziel i zwyciężaj": dla danego typu problemu (jak testowanie własności aerodynamicz-
nych lub szkolenie pilotów) budujemy modele skupiające się jedynie na zagadnieniach
istotnych dla tego problemu. Generalnie każdy z tych modeli powinien być już na tyle pro-
sty, że możliwy do ogarnięcia przez pojedynczą osobę. Zgodnie ze wskazówką „7 ± 2" poda-
ną przez Millera [Miller, 1956], model taki powinien obejmować od 5 do 9 części.
Modele posiadają ponadto istotną zaletę, która dodatkowo zwiększa ich rolę w zma-
ganiu się ze złożonością problemów: otóż modele, początkowo niedoskonałe, można suk-
cesywnie ulepszać, co sprawia, że stają się bardziej szczegółowe i bliższe rzeczywistości. W in-
żynierii oprogramowania — jak zresztą w większości dyscyplin inżynierskich — modele
poprzedzają zazwyczaj powstanie rzeczywistego systemu. I tak na etapie analizy budujemy
najpierw model otoczenia systemu i jego ogólnej funkcjonalności, jaką ma prezentować —
model na poziomie zrozumiałym przez klienta. Model ten następnie rozwijamy, dodając doń
więcej szczegółów, takich jak zestaw wyświetlanych formularzy, układ interfejsu użytkownika,
zachowanie się w sytuacjach wyjątkowych i tym podobne. Zbiór wszystkich takich modeli,
stworzonych w czasie powstawania systemu, składa się na ogólny, całościowy system model.
Gdybyśmy nie korzystali z modelowania, lecz od razu przystąpilibyśmy do pisania kodu
źródłowego, musielibyśmy ze szczegółami określić wszelkie szczegóły interfejsu użytkownika,
2.3. Podstawowe koncepcje modelowania 71

zanim jeszcze zdążylibyśmy zapytać klienta o cokolwiek. Nieuniknione w tej sytuacji póź-
niejsze korekty i poprawki — jako skutek zmieniających się wymagań klienta — byłyby bardzo
pracochłonne i zajmowałyby dużo czasu.
Niestety, często same modele bywają bardzo skomplikowane i trudne do zrozumienia.
Na szczęście, paradygmat „dziel i zwyciężaj" i tutaj przychodzi z pomocą, dostarczając kolejny
środek upraszczający — widoki. Widok to podzbiór wybranych cech modelu, bardziej zrozu-
miały niż model ujmowany całościowo. Jak pokazaliśmy to na rysunku 2.6, zbiór podręczników
i instrukcji serwisowych związanych z samolotem jest właśnie takim podzbiorem, podobnie jak
system paliwowy czy system okablowania. Jest zrozumiałe, że widoki nie muszą być rozłączne
— na przykład elementem okablowania samolotu jest okablowanie jego systemu paliwowego.

Rysunek 2.6. Model jest abstrakcją opisującą podzbiór systemu, podobnie widok ukazuje tylko wybrany
aspekt modelu. Poszczególne widoki tego samego modelu mogą nakładać się na siebie

Notacja to tekstowe lub graficzne reguły reprezentowania widoków. Diagram klas jest
graficznym widokiem modelu obiektowego. Na diagramie okablowania samolotu każda linia
ciągła reprezentuje przewód lub wiązkę przewodów. Na diagramie klas prostokąt opatrzony
tytułem reprezentuje klasę, zaś linia łącząca dwa prostokąty odpowiada relacji istniejącej mię-
dzy odnośnymi klasami. Zauważmy, że ten sam widok może być reprezentowany w formie
różnych notacji, czego przykładem jest rysunek 2.7.

Rysunek 2.7. Przykład przedstawienia modelu w formie dwóch różnych notacji. Na wspomniany
model składają się dwie klasy: Książka i Rozdział, połączone oczywistą zależnością „Książka składa się
z rozdziałów". W notacji UML klasy przedstawione są w formie prostokątów, zaś agregacja symboli-
zowana jest przez linię zakończoną małym rombem. W notacji Boocha natomiast klasy przedstawiane
są w formie chmur, zaś linia reprezentująca agregację zakończona jest zaczernionym kółeczkiem
72 Rozdział 2. • Modelowanie w języku UML

Na gruncie inżynierii oprogramowania obok UML funkcjonuje wiele różnych notacji


używanych do modelowania systemów. Podczas gdy UML ujmuje system w kategoriach klas,
zdarzeń, stanów, interakcji i aktywności, diagramy De Marco [De Marco, 1978] uwidoczniają
procesy wyszukiwania, przetwarzania i magazynowania danych, a Z-Schematy [Spivey, 1992]
postrzegają system w charakterze niezmienników (czyli warunków, które permanentnie
pozostają niezmienne) oraz warunków pozostających prawdziwymi przed i po wykonaniu
danej operacji. Każda z tych (i innych) notacji bywa szczególnie przydatna dla określonej
klasy problemów.
W kolejnych sekcjach przyjrzymy się bardziej szczegółowo procesowi modelowania.

2.3.2. Typy danych, abstrakcyjne typy danych i instancje


Typ danych jest abstrakcją istniejącą w kontekście języka programowania. Opatrzony jest
unikalną nazwą, która odróżnia go od innych typów danych i wyznacza zbiór wartości, jakie
przyjmować mogą jego instancje, zwane też wystąpieniami lub egzemplarzami. Integralną
częścią definicji typu danych jest określenie struktury jego instancji oraz zestawu operacji,
jakim instancje te mogą być poddawane. Języki z wbudowaną kontrolą typów danych (zwane
typed languages) traktują naruszenie tych reguł jak błędy składniowe.
I tak w języku Java nazwa i n t odnosi się do typu obejmującego liczby całkowite
z przedziału od -2 3 2 do +2 3 2 -l (włącznie). Dopuszczalnymi operacjami dla tego typu są te,
które wynikają z arytmetyki całkowitoliczbowej — czyli między innymi dodawanie, odej-
mowanie, mnożenie i dzielenie — oraz funkcje i metody posiadające parametr(y) typu i n t
(jak na przykład mod zwracające resztę z dzielenia). Biblioteka Javy spowoduje wygenero-
wanie wyjątku przy próbie wykonania operacji zmiennopozycyjnej (na przykład trunc czy
f 1 oor) na liczbie całkowitej.
Abstrakcyjny typ danych to typ definiowany w specyfikacji niezależnej od konkretnej
implementacji. Niezależność taka umożliwia programistom operowanie na bardziej ogólnym
poziomie abstrakcji, bez nawiązywania do szczegółów składniowych czy semantycznych
konkretnego języka programowania. Przykładem abstrakcyjnych typów danych są zbiory
(w sensie teorii mnogości) i sekwencje — w czysto matematycznym rozumieniu. Konkretny
język programowania może dostarczać różne mechanizmy ich implementowania, często
także w różnych wariantach optymalizowanych pod kątem określonego kryterium (wydaj-
ność wyszukiwania lub wstawiania elementu, zajętość pamięci i tym podobne), jednak w pew-
nych sytuacjach programista zainteresowany jest tylko semantyką zbioru jako takiego,
w oderwaniu od jego konkretnej realizacji. Przykładowo abstrakcyjny typ danych Pracowni k
może definiować operacje podaj Nazwisko ()', podaj NrNIP() czy podajAdres(). To, czy numer
NIP jest w konkretnej implementacji zapamiętywany jako liczba całkowita czy jako łańcuch,
może w danym kontekście nie mieć znaczenia dla reszty systemu. Decyzje o wyborze kon-
kretnej implementacji danego typu stanowią szczególny przypadek ogólnie pojmowanych
decyzji implementacyjnych.

1
Odwołania do operacji będą mieć w tej książce postać nazwy, po której następuje lista parametrów
zamknięta w nawias; dla operacji bezargumentowych jest to nawias pusty. Operacjami zajmiemy
się szczegółowo w następnej sekcji.
2.3. Podstawowe koncepcje modelowania 73

2.3.3. Klasy, klasy abstrakcyjne i obiekty


Klasa jest abstrakcją na gruncie zarówno modelowania zorientowanego obiektowo, jak i na
gruncie obiektowego języka programowania. Podobnie jak abstrakcyjne typy danych, klasy
zamykają w sobie („enkapsulują") i strukturę danych, i ich zachowanie. W przeciwieństwie
jednak do „zwykłych" abstrakcyjnych typów danych, klasy mogą być definiowane na bazie
innych Idas za pomocą mechanizmu zwanego dziedziczeniem. Załóżmy na przykład, że nasz
prosty zegarek z poprzednich przykładów postanowiliśmy rozszerzyć o funkcję kalkulatora.
Reprezentująca ów nowy model (nomen omen) zegarka klasa Cal cul atorWatch może być
uważana za rozwiniętą wersję ldasy Watch, po której dziedziczy wszystkie własności, dodając
własne, specyficzne. Klasę Watch, jako bardziej ogólną, nazywamy w tym przypadku nad-
klasą łub superklasą2, zaś klasę rozwiniętą (CalculatorWatch) — podklasą lub subklasą3.
Zgodnie z diagramem przedstawionym na rysunku 2.8, klasa Cal cul atorWatch definiuje ele-
menty funkcjonalne związane z wykonywaniem prostych działań arytmetycznych, zaś klasa
Watch elementów tych — oczywiście — nie posiada. Pojęcia superklasy i subklasy są względne:
dana klasa może być subklasą jednej klasy i jednocześnie stanowić superklasę dla innych klas.

Rysunek 2.8. Diagram klas prezentujący dwie powiązane klasy — Watch i CalculatorWatch. Klasa
Cal cul atorWatch stanowi rozwinięcie klasy Watch o elementy typowe dla kalkulatora, niespotykane
w „normalnych" zegarkach. Na diagramach UML klasy reprezentowane są przez prostokąty podzielone
w pionie na trzy części: w najwyższej z nich znajduje się nazwa klasy, w środkowej — jej atrybuty, w naj-
niższej natomiast wyszczególnione są jej operacje. W niektórych przypadkach dwie ostatnie części po-
mijane są ze względów prostoty lub przejrzystości. Relacja dziedziczenia odzwierciedlona jest przez li-
nię zakończoną małym trójkątem, zlokalizowanym po stronie superklasy

Jeżeli klasa pełni jedynie rolę generalnego wzorca dla swych subklas, dostarczając im
wspólnych atrybutów i operacji, a więc gdy nie przewiduje się tworzenia jej instancji (obiek-
tów), nazywamy ją klasą abstrakcyjną. Klasy abstrakcyjne używane są do reprezentowania
ogólnych koncepcji dziedziny aplikacyjnej, a ich nazwy wyróżniane są na diagramach pochy-
loną czcionką. W diagramie na rysunku 2.9 klasa Benzen reprezentująca związek chemiczny
o tej samej nazwie i o określonej strukturze molekularnej jest subklasą klasy ZwiqzekOrganiczny,
reprezentującej jakąś substancję chemii organicznej w ogólności, bez jakiejś konkretnej struk-
tury molekuł.

2
Często zwana także „klasą bazową" lub „klasą macierzystą" — przyp. tłum.
' Często nazywana też „klasą pochodną" — przyp. tłum.
74 Rozdział 2. • Modelowanie w języku UML

Rysunek 2.9. Przykład klasy abstrakcyjnej: klasa ZwiązekOrganiczny nie reprezentuje żadnego konkret-
nego związku chemicznego, istnieje tylko jako reprezentant wszystkich związków organicznych w ogólności.
Jej nazwa, jako nazwa klasy abstrakcyjnej, pisana jest na diagramie klas kursywą

W języku Java klasa abstrakcyjna Col 1 ecti on uosabia wspólne cechy wszystkich kolekcji
i pełni rolę superldasy dla klas reprezentujących konkretne rodzaje kolekcji, między innymi
Li nkedLi s t , ArrayLi s t i HashMap. W przeciwieństwie do tych ostatnich, klasa Col 1 ecti on jest
zbyt ogólna na to, by można było tworzyć jej instancje. Należy w tym miejscu zauważyć, że nie
wszystkie klasy generalizujące pewną funkcjonalność są klasami abstrakcyjnymi — nie jest
na przykład klasą abstrakcyjną klasa Watch z diagramu na rysunku 2.8. Na gruncie modelo-
wania systemów informatycznych klasy abstrakcyjne niekiedy nie posiadają odpowiedników
w postaci koncepcji z dziedziny aplikacyjnej, lecz wprowadzane są do modelu raczej z myślą
o zredukowaniu jego złożoności czy też wielokrotnym wykorzystaniu pewnych wzorców.
Częścią definicji klasy jest definicja jej operacji, którym poddawane są instancje klasy.
Operacja zdefiniowana w superklasie może być dziedziczona przez subklasę i stosowana do
jej obiektów. Przykładowo w diagramie na rysunku 2.8 operacja SetDate(d), odpowiadająca
ustawieniu konkretnej daty w zegarku reprezentowanym przez klasę Watch, stosuje się także
do obiektów klasy Cal cul atorWatch. Dla odróżnienia, definiowana w subklasie operacja
EnterCal cMode() nie ma sensu w odniesieniu do klasy Watch.
Atrybutem klasy jest jej nazwana własność, określona w odniesieniu do każdego jej
obiektu z osobna. Każdy atrybut ma w ramach klasy unikalną nazwę i posiada określony typ.
Klasa Watch posiada atrybuty time i date; klasa Cal cul atorWatch, dziedzicząc te atrybuty ze
swej superldasy, dodaje do nich atrybut cal cul a t o r S t a t e .
Obiekt jest instancją klasy. Każdy obiekt jest jednoznacznie identyfikowany i przecho-
wuje określony zbiór wartości atrybutów swej klasy. Każdy obiekt należy do dokładnie jednej
klasy. Na diagramach UML obiekty reprezentowane są w postaci prostokątów, a ich nazwa jest
podkreślona. Podkreślenie ma na celu odróżnienie obiektów od Idas4. W diagramie na rysunku
2.10 obiekt simpl eWatchl291 jest instancją klasy Watch, a obiekt calculatorWatchl515 jest
instancją klasy Cal cul atorWatch. Warto zwrócić uwagę na pewien ważny fakt: mimo iż obiekt
cal cul atorWatchl515 nie jest instancją klasy Watch, stosują się do niego operacje tej klasy.
Atrybuty obiektu mogą być widoczne dla innych części systemu, zależnie od konkretnego
języka programowania; język Java umożliwia bardzo szczegółowe definiowanie widoczności
poszczególnych atrybutów.

4
Podkreślenia używamy także do oznaczenia lokalizatorów URL, zatem w celu polepszenia czytelności
nie podkreślamy nazw obiektów w tekście otwartym, przez co stają się one nieodróżnialne od nazw klas.
Staramy się jednak przy tym, by zawsze w takich sytuacjach jasno wynikało z kontekstu, czy mamy do
czynienia z klasą, czy też z konkretnym obiektem. W diagramach na rysunkach podkreślenia są jednak
konsekwentnie stosowane.
2.3. Podstawowe koncepcje modelowania 75

Rysunek 2.10. Dwie klasy i ich instancje na diagramie UML. Mimo iż operacje klasy Watch stosują się
także do obiektu cal cul atorWatchl515, ten nie jest instancją klasy Watch

2.3.4. Klasy zdarzeniowe, zdarzenia i komunikaty


Klasa zdarzeniowa to abstrakcja reprezentująca zbiór zdarzeń, na które system przejawiać
powinien określoną, wspólną reakcję. Zdarzenie jest instancją klasy zdarzeniowej i ma swój
odpowiednik w postaci na przykład impulsu ze strony aktora („WatchUser nacisnął lewy
przycisk"), upływu czasu („minęły 2 minuty") lub wymiany komunikatów między obiektami.
Wysłanie komunikatu oznacza żądanie (ze strony jednego obiektu) wykonania pewnej ope-
racji przez obiekt docelowy. Komunikat składa się z nazwy i pewnego ciągu parametrów; gdy
obiekt docelowy odbierze komunikat, kojarzy jego nazwę z jedną ze swych operacji i wykonuje
tę operację, przekazując jej otrzymane w komunikacie parametiy. Ewentualny wynik operacji
przesyłany jest z powrotem do obiektu nadawcy.
W diagramie na rysunku 2.11 obiekt Watch, w celu wyświetlenia bieżącego wskazania
czasu, zwraca się najpierw do obiektu Time o udostępnienie aktualnej wartości czasu GMT,
a następnie żąda od obiektu TiraeZone dostarczenia różnicy (przesunięcia) między czasem lo-
kalnym a czasem GMT; zsumowanie tych dwóch wartości da w wyniku wartość, która zo-
stanie wyświetlona.

Rysunek 2.11. Przykłady komunikatów przesyłanych między obiektami. Obiekt Watch wysyła do obiektu
Time komunikat getTime() oznaczający żądanie udostępnienia bieżącej wartości czasu GMT, po czym
wysyłając do obiektu TimeZone komunikat getTimeDeltaQ, żąda wartości przesunięcia czasu lokalnego
dla bieżącej strefy czasowej. Otrzymane zwrotnie wyniki (fakt ich przesiania zaznaczony jest linią prze-
rywaną ze strzałką) są sumowane, co daje wartość odpowiednią do wyświetlenia
76 Rozdział 2. • Modelowanie w języku UML

Zdarzenia i komunikaty są instancjami: reprezentują konkretne wydarzenia w ramach


systemu. Klasy zdarzeniowe są natomiast abstrakcjami opisującymi grupy zdarzeń wywołujące
jednolite relacje ze strony systemu. W praktyce jednak termin „zdarzenie" może odnosić się
zarówno do obiektu (instancji), jak i do klasy zdarzeniowej — właściwe znaczenie można jednak
odczytać z kontekstu, w którym się pojawia.

2.3.5. Modelowanie zorientowane obiektowo


Dziedzina aplikacyjna reprezentuje wszelkie aspekty problemu użytkownika: jego fizyczne
środowisko, potencjalnych użytkowników systemu i inne osoby, czynności, które system ma
usprawniać i tak dalej. Zrozumienie wszelkich subtelności dziedziny aplikacyjnej przez anali-
tyków i programistów jest niezbędne, by tworzone produkty zgodne były z oczekiwaniami
użytkowników. Nie zapominajmy przy tym, że dziedzina aplikacyjna zmienia się z biegiem
czasu, w miarę jak użytkownicy coraz lepiej uświadamiają sobie swoje potrzeby i zmienia się
ich środowisko 3 .
Dziedzina realizacyjna to przestrzeń modelowania wszystkich możliwych systemów.
Modelowanie w tej przestrzeni obejmuje aktywności typowe dla projektu systemu i pro-
jektu obiektów. Model dziedziny realizacyjnej jest znacznie bogatszy od modelu dziedziny
aplikacyjnej, ma też zwykle bardziej ulotny charakter. Jest to prosta konsekwencja faktu, że
sam system modelowany jest na poziomie znacznie bardziej szczegółowym niż dziedzina
aplikacyjna. Pojawiające się nowe technologie (zwane także „nowinkami technologicznymi"),
lepsze rozumienie technik implementacyjnych przez programistów, zmiany w wymaganiach
— wszystko to jest motorem napędowym zmian w modelach dziedziny realizacyjnej. Za-
uważmy również, że wdrażanie systemu może mieć wpływ na zmiany w dziedzinie aplika-
cyjnej, jeśli użytkownicy wypracują nowe sposoby postępowania w związku z otrzymaniem
tego systemu.
Analiza zorientowana obiektowo powiązana jest z dziedziną aplikacyjną: aktywno-
ści modelowania obu tych kategorii reprezentowane są w ten sam sposób — poprzez klasy
i obiekty. Na gruncie obiektowo zorientowanej analizy model dziedziny aplikacyjnej staje
się częścią modelu systemu. Przykładowo częścią systemu kontroli ruchu lotniczego jest klasa
T r a f i c C o n t r o l l e r , reprezentująca poszczególnych użytkowników (kontrolerów) wraz z ich
osobistymi ustawieniami i bieżącą informacją na temat ruchu. W systemie tym istnieje także
klasa Ai r c r a f t , reprezentującą samoloty objęte kontrolą. Obie wymienione klasy należą do
modelu dziedziny aplikacyjnej i kodowane są w ramach systemu (patrz rysunek 2.12).
Używanie tej samej notacji dla modelowania zarówno dziedziny aplikacyjnej, jak i dzie-
dziny realizacyjnej ma swe wady i zalety. Jest korzystne, bowiem odpowiedniość koncepcji
aplikacyjnych i poszczególnych klas dziedziny realizacyjnej umożliwia mapowanie tych ostat-
nich z powrotem w dziedzinę aplikacyjną; co więcej, klasy te mogą być hermetyzowane w ramach
podsystemów niezależnie od innych koncepcji implementacyjnych (na przykład interfejsu

5
Dziedzina aplikacyjna może podlegać dalszemu podziałowi na dziedzinę użytkownika i dziedzinę
klienta. Pierwsza obejmuje wszelkie zagadnienia znaczące dla użytkownika systemu — funkcjonalność,
wygodę użytkowania, prostotę obsługi oraz łatwość nauczenia się jej i tym podobne; do dziedziny
klienta należą natomiast takie aspekty systemu jak koszt jego wytworzenia, ogólny koszt posiadania
i konserwacji czy też wpływ systemu na całokształt organizacji firmy.
2.3. Podstawowe koncepcje modelowania 77

Rysunek 2.12. Model dziedziny aplikacyjnej reprezentuje encje środowiska związane z kontrolą ruchu
lotniczego (między innymi samoloty i kontrolerów). Model systemu reprezentuje natomiast encje
stanowiące część tego systemu — wyświetlacz map, bazę rozkładu lotów i tym podobne. W ramach
obiektowo zorientowanej analizy i projektu model dziedziny aplikacyjnej staje się częścią modelu
systemu. Innymi słowy, model systemu stanowi rozwinięcie modelu dziedziny aplikacyjnej o koncepcje
pochodzące z dziedziny realizacyjnej, reprezentowane między innymi przez klasy SummaryDisplay,
MapDispl ay i FI ightPl anDatabase (do tej kwestii powrócimy w rozdziale 5. „Analiza wymagań")

użytkownika czy technologii bazodanowych), co otwiera drogę do tworzenia zestawów narzę-


dziowych wielokrotnego użytku, powiązanych wprost z określonymi aspektami dziedziny apli-
kacyjnej. Niestety, wspólna notacja dla modelowania obu dziedzin usuwa granicę między
nimi, czyli między światem rzeczywistym a jego modelem. W efekcie utrudnia to upraszczanie
dziedziny realizacyjnej i jej polaryzację w kierunku ostatecznego rozwiązania. Świadomi tej
niedogodności używamy wspólnej notacji dla obu dziedzin, wprowadzając jednak wyraźne
rozróżnienie między nimi, gdy pojawiają się niejednoznaczności. W większości przypadków
odwołujemy się do modeli (i na przykład stwierdzenie „Ai r c r a f t jest skojarzone z FI i ghtPl an"
odnosi się do klas dziedziny realizacyjnej, a nie do koncepcji dziedziny aplikacyjnej).

2.3.6. Falsyfikacja i prototypowanie


Istotą modelowania jako upraszczania rzeczywistości jest ignorowanie nieistotnych aspektów
problemu; aspekty istotne w danym kontekście muszą być jednak w modelu reprezentowane.
Falsyfikacja [Popper, 1992] jest procesem demonstracji, że pewne istotne szczegóły pro-
blemu są w modelu reprezentowane niewłaściwie lub nie są reprezentowane w ogóle — czyli że
model jest nieadekwatny do rzeczywistości, którą ma reprezentować.
78 Rozdział 2. • Modelowanie w języku UML

Proces falsyfikacji jest dobrze znanym elementem naukowego opisywania rzeczywisto-


ści. Naukowcy proponują różne jej modele, które zdobywają coraz większą wiarygodność,
w miarę jak pojawia się coraz więcej danych świadczących na rzecz ich prawdziwości, by
odrzucić dany model, gdy pojawi się choć jeden fakt świadczący na jego niekorzyść. I tak
na przykład u schyłku XVIII wieku okazało się, że orbita Merkurego ma postać różną od
tej, jaką przewiduje prawo grawitacji Izaaka Newtona. Minęło ponad 100 lat i Albert Ein-
stein zaproponował swą ogólną teorię względności, która różnicę tę znakomicie tłumaczyła.
Teoria Newtona została więc obalona na rzecz teorii Einsteina. Mimo to, wciąż wykorzy-
stujemy teorię Newtona do praktycznego wyjaśniania wielu zjawisk zachodzących na Ziemi,
bowiem stopień jej nieadekwatności do otaczającego świata jest szczegółem nieistotnym
w skali, w jakiej zjawiska te zachodzą; jest ona za to znacznie prostsza od ogólnej teorii
względności.
Falsyfikację możemy z powodzenie stosować także w inżynierii oprogramowania. Jedną
z technik tworzenia systemów informatycznych jest prototypowanie: projektując interfejs
użytkownika, programiści najpierw budują jego prototyp, symulujący jedynie ostateczną
postać. Prototyp ten jest następnie prezentowany potencjalnym użytkownikom w celu oceny
— czyli przeważnie falsyfikacji — i odpowiednio modyfikowany.
W pierwszej iteracji tego procesu początkowa wersja prototypu zostanie prawdopodob-
nie porzucona w rezultacie konsultacji z użytkownikami. Innymi słowy, użytkownicy dokonają
falsyfikacji prototypu — będącego jednym z modeli systemu — ponieważ nie reprezentuje on
właściwie detali, które są dla nich istotne.
Niestety, nie istnieje podobna metoda kategorycznego zaakceptowania modelu (podob-
nie jak nie istnieje metoda udowodnienia, że program jest bezbłędny). I nawet jeśli w niektó-
rych sytuacjach możliwe jest matematyczne wykazanie równoważności dwóch modeli, nie
sposób matematycznie udowodnić, że którykolwiek z nich prawidłowo reprezentuje rzeczy-
wistość. Owszem, formalne techniki weryfikacji poprawności programów mogą co najwyżej
przekonać programistów, że specyficzna implementacja systemu jest spójna z jego formalną
specyfikacją, jednakże tylko testowanie przez specjalistów z dziedziny aplikacyjnej i intensywna
eksploatacja systemu mogą skonfrontować jego zgodność z potrzebami klienta. Model sys-
temu może być w każdym czasie zakwestionowany (sfalsyfikowany) w rezultacie zmieniają-
cych się wymagań, pojawienia się nowych technologii implementacyjnych czy też zmian
w środowisku tego systemu.

2.4. UML — głębszy wgląd


Opiszemy teraz bardziej szczegółowo pięć głównych typów diagramów UML, które czytelnicy
napotykać będą dalej w tej książce.

• Diagramy przypadków użycia — reprezentują funkcjonalność systemu z perspek-


tywyjego użytkowników, ukazują granice systemu; poświęcamy im sekcję 2.4.1.
• Diagramy klas — reprezentują statyczną strukturę systemu w kategoriach obiektów,
ich atrybutów, operacji i powiązań; opisujemy je w sekcji 2.4.2.
2.4. UML. — głębszy wgląd 79

• Diagramy interakcji — przedstawiają obraz zachowania systemu w kategoriach


interakcji między obiektami; wykorzystywane są do identyfikowania obiektów zarów-
no w ramach dziedziny aplikacyjnej, jak i dziedziny implementacyjnej; są przedmiotem
sekcji 2.4.3.
• Diagramy stanów — reprezentują zachowanie każdego z istotnych obiektów; przed-
stawiamy je w sekcji 2.4.4.
• Diagramy aktywności — ukazują przepływ sterowania lub danych w systemie; opi-
sujemy je w sekcji 2.4.5.

2.4.1. Diagramy przypadków użycia

Przypadki użycia i aktorzy

Aktorzy to zewnętrzne encje przejawiające interakcję z systemem. Aktorem może być


użytkownik obsadzony w określonej roli (administrator systemu, klient banku, pracownik
banku) lub inny system (centralna baza danych, linia produkcyjna). Aktorzy identyfikowani
są za pomocą unikalnych nazw i opatrzeni opisami.
Przypadki użycia opisują zachowanie systemu z punktu widzenia aktorów. Z tego
względu zachowanie systemu opisywane w ramach przypadków użycia nazywane jest za-
chowaniem zewnętrznym. Każdy przypadek użycia dedykowany jest określonej funkcji
systemu, widzianej jako zbiór zdarzeń dających skutki widoczne dla aktorów. Aktorzy inicjują
przypadki użycia, korzystając ze wspomnianych funkcji; zainicjowany przypadek użycia może
uruchamiać inne przypadki użycia, wymagające od aktorów nowych informacji. Wymianę
informacji między aktorami a przypadkami użycia nazywamy komunikacją. W dalszym ciągu
pokażemy odwzorowanie tej wymiany w postaci relacji komunikacyjnych między obiektami.
I tak na przykład w systemie zarządzania sytuacjami kryzysowymi (wypadkami) funk-
cjonariusze policji oraz strażacy komunikują się z dyspozytorem za pomocą urządzeń mo-
bilnych. Dyspozytor ma czytelną informację na temat stanu dostępnych zasobów (na przykład
wozów strażackich) i dysponuje nimi, wydając polecenia ze swej stacji roboczej. Z perspektywy
przypadków użycia związanych z sytuacją kryzysową dyspozytor i funkcjonariusz straży
pożarnej (lub policji) mogą być modelowani jako aktorzy.
W diagramie przedstawionym na rysunku 2.13 aktor FieldOfficer (funkcjonariusz) ini-
cjuje przypadek użycia ReportEmergency, którego istotą jest powiadomienie aktora Dispatcher
(dyspozytora) o zaistniałym zdarzeniu. W odpowiedzi aktor Dispatcher inicjuje przypadek
użycia Openlncident, obejmujący sporządzenie raportu o sytuacji kryzysowej i uruchomienie
stosownych procedur awaryjnych. Dispatcher wprowadza otrzymane od aktora FieldOfficer
informacje do bazy aplikacji zarządzającej (FRIEND — First Responder Interactive Emergency
Navigational Database, interaktywna nawigacyjna baza danych dla pierwszego reagowania)6
i wprowadza do akcji niezbędne zasoby w ramach przypadku użycia Al 1 ocateResources.

6
Patrz http://wwwxs.cmu.edu/afs/andrew.cmu.edu/inst/ini/www/WIRELESS/FRIEND/index.html
— przyp. tłum.
80 Rozdział 2. • Modelowanie w języku UML

Rysunek 2.13. Przykład przypadku użycia dla bazy FRIEND systemu zarządzania sytuacjami kryzysowymi.
Skojarzenia między aktorami i przypadkami użycia oznaczają przepływ informacji. Skojarzenia te mają
charakter dwukierunkowy: mogą oznaczać inicjowanie przypadku użycia przez aktora (na przykład za-
inicjowanie przypadku ReportEmergency przez aktora F i e l d O f f i c e r ) lub dostarczenie informacji
aktorowi ( D i s p a t c h e r jest informowany w ramach przypadku użycia ReportEmergency). Prostokąt
zamykający przypadki użycia wyznacza granice systemu

Opisując przypadek użycia w formie tekstowej, wykorzystywać będziemy szablon skła-


dający się z sześciu pól, taki jak w tabeli 2.1, zaczerpnięty z książki Constantina i Lockwood
[Constantine i Lockwood, 2001]. Pola te określają:

• nazwę, unikalną w skali całego systemu, jednoznacznie identyfikującą przypadek


użycia dla wszystkich uczestników i programistów,
• listę aktorów współdziałających z przypadkiem użycia,
• zbiór warunków wstępnych, których spełnienie konieczne jest do zainicjowania
przypadku użycia,
• przepływ zdarzeń, czyli listę poszczególnych interakcji aktorów z przypadkiem
użycia, ponumerowanych dla celów identyfikacji; zdarzenia „normalne" (czyli
spodziewane przez użytkownika) i zdarzenia wyjątkowe (błędy i inne nieoczekiwane
sytuacje) dla czytelności opisywane są w ramach odrębnych przypadków użycia; opis
przepływu zdarzeń ma postać dwukolumnową: w lewej kolumnie reprezentowa-
ne są kroki podejmowane przez aktorów, prawa kolumna przeznaczona jest na zda-
rzenia zachodzące w ramach systemu; każda para takich kroków opisuje pojedynczą
interakcję,
• zbiór warunków końcowych, czyli takich, których spełnienie jest gwarantowane po
zrealizowaniu przypadku użycia,
• wymagania jakościowe, niezwiązane bezpośrednio z funkcjonalnością systemu.
Należą do nich między innymi wymagania pod względem wydajności systemu, jego
implementacji, jego platformy sprzętowej i tym podobne. Wymagania takie opiszemy
szczegółowo w rozdziale 4. „Zbieranie wymagań".
2.4. UML. — głębszy wgląd 81

Tabela 2.1. Przykładowy przypadek użycia — ReportEmergency

Nazwa przypadku użycia ReportEmergency

Aktorzy uczestniczący Fi el dOf f i c e r — inicjuje przypadek użycia

Di s p a t c h e r — komunikuje się z przypadkiem użycia

Przepływ zdarzeń 1. Fi el dOf f i c e r aktywuje funkcję „Raportuj zdarzenie" na swym


terminalu
2.System FRIEND wyświetla w odpowiedzi stosowny
formularz

3. Fi el dOf f i c e r wypełnia formularz, ustalając typ zdarzenia,


stopień zagrożenia, jego lokalizację i krótki opis sytuacji.
Fi el dOf f i c e r określa także możliwe sposoby pierwszej reakcji
na zagrożenie. Po wypełnieniu formularza Fi el dOf f i c e r wysyła
go do systemu.
4. System FRIEND powiadamia dyspozytora
( D i s p a t c h e r ) o zdarzeniu.
5. Di s p a t c h e r analizuje informacje zawarte w formularzu, po czym
tworzy w bazie nowy obiekt I n c i d e n t , realizując p r z y p a d e k
użycia Openlnci d e n t . Di s p a t c h e r określa sposób reakcji
i zatwierdza raport.

6. System FRIEND i n f o r m u j e aktora Fi el dOf f i c e r


o zatwierdzeniu raportu i przekazuje m u informację
od dyspozytora.
Warunki wstępne • Fi el dOf f i cer jest zalogowany do systemu FRI END

Warunki końcowe • Fi el dOf f i c e r otrzymał potwierdzenie przesłania r a p o r t u


oraz wiadomość od dyspozytora (Di spatcher)
InH
lUD
• Fi el dOf f i cer otrzymał od systemu wyjaśnienie, dlaczego
transakcja nie może zostać zrealizowana

Wymagania jakościowe • Raport aktora Fi el dOf f i c e r zostaje potwierdzony w czasie


nie dłuższym niż 30 sekund

• Fi el dOf f i c e r o t r z y m u j e o d p o w i e d ź nie później niż


30 sekund po wysłaniu jej przez dyspozytora

P r z y p a d k i użycia z a p i s y w a n e są w j ę z y k u n a t u r a l n y m . Jest to n i e z b ę d n e z p e r s p e k t y w y
k o m u n i k a c j i p r o g r a m i s t ó w z u ż y t k o w n i k a m i , u k t ó r y c h z reguły n i e m o ż n a zakładać z n a j o -
m o ś c i t e r m i n o l o g i i i n o t a c j i p r o g r a m i s t y c z n e j . Użycie języka z r o z u m i a ł e g o dla wszystkich m a
jeszcze tę zaletę, że u m o ż l i w i a z r o z u m i e n i e w y m a g a ń s y s t e m u p r z e z u c z e s t n i k ó w r e p r e z e n t u -
j ą c y c h dyscypliny w i e d z y i n n e niż inżynieria o p r o g r a m o w a n i a czy n a w e t dziedzina aplikacyj-
n a . P o n a d t o w j ę z y k u n a t u r a l n y m często wiele rzeczy d a się w y r a ż a ć z g r a b n i e j i czytelniej n i ż
za p o m o c ą d i a g r a m ó w .
W d i a g r a m a c h p r z y p a d k ó w użycia w y s t ę p u j ą cztery r o d z a j e zależności: k o m u n i k a c j a ,
z a w i e r a n i e , r o z s z e r z e n i e i d z i e d z i c z e n i e . P r z y j r z y j m y i m się n i e c o d o k ł a d n i e j .
82 Rozdział 2. • Modelowanie w języku UML

Zależności komunikacyjne

Aktor i przypadek użycia komunikują się, gdy między nimi wymieniana jest informacja.
Relacja komunikacji reprezentowana jest w diagramie za pomocą linii ciągłej łączącej
aktora z przypadkiem użycia. W diagramie z rysunku 2.13 aktorzy Fi el dOf f i cer i Di spatcher
komunikują się z przypadkiem użycia ReportEmergency. Aktor Dispatcher (i tylko on) ko-
munikuje się ponadto z przypadkami użycia Openlncident i Al 1 ocateResources. Z relacji
komunikacyjnych można odczytywać dostęp aktorów do poszczególnych elementów funk-
cjonalnych systemu: we wspomnianym diagramie obaj aktorzy — Fi el dOf f i cer i Di spatcher
posługują się odmiennymi interfejsami i mają dostęp do różnych funkcji systemu.

Relacja zawierania

W skomplikowanym systemie modele przypadków użycia mogą być również bardzo


złożone. Jednym z oczywistych sposobów redukowania złożoności jest eliminowanie redun-
dancji: elementy i konstrukcje występujące w przypadku użycia wielokrotnie są wówczas wy-
odrębniane w postaci osobnych przypadków użycia. Dyspozytor (Di spatcher) z naszego przy-
kładu ma możliwość wyświetlenia na ekranie planu miasta w wyniku naciśnięcia odpowiedniego
klawisza; ponieważ czynność ta będzie prawdopodobnie elementem kilku przypadków użycia
(między innymi Openlncident i Al 1 ocateResources), wyodrębnimy ją w postaci przypadku
użycia ViewMap, który dołączony zostanie do wyżej wymienionych przypadków (i prawdo-
podobnie wielu innych), czyli zostanie w nich zawarty.
Funkcjonalność reprezentowana przez przypadek użycia ViewMap będzie opisana w mo-
delu jednokrotnie, co zmniejszy jego redundancję i sprawi, że stanie się mniej złożony. Gdy
jeden przypadek użycia jest częścią drugiego — czyli gdy przepływ zdarzeń w ramach jednego
z nich jest częścią przepływu zdarzeń drugiego — mówimy, że przypadki te połączone są relacją
zawierania. Na diagramie relacja ta odzwierciedlona jest w postaci linii przerywanej opa-
trzonej etykietą «i ncl ude» i zakończonej strzałką skierowaną w stronę przypadku zawieranego
— czego przykład widzimy na rysunku 2.14.

Rysunek 2.14. Przykład relacji zawierania w diagramie przypadków użycia

W tekstowym opisie przypadków użycia relację zawierania uwzględniać będziemy na


dwa sposoby. Jeśli wyodrębniony przypadek użycia będzie mógł być dołączany w dowolnym
punkcie przepływu zdarzeń (jak przypadek Vi ewMap), fakt jego dołączania ujmiemy w wierszu
Wymagania jakościowe (tak jak w tabeli 2.2); jeśli przypadek ten wywoływany będzie w kon-
kretnych punktach przepływu zdarzeń, zaznaczać to będziemy w sposób jawny.
2.4. UML. — głębszy wgląd 83

Tabela 2.2. Tekstowa reprezentacja relacji zawierania z rysunku 2.14 (słowo „dołączyć" wyszczególnione
jest czcionką pogrubioną)

Nazwa przypadku użycia Al l o c a t e R e s o u r c e s

Aktor uczestniczący Di s p a t c h e r — inicjuje przypadek użycia

Przepływ zdarzeń [...]

Warunki wstępne • Di spatcher otwiera realizację zdarzenia (Inci dent)

Warunki końcowe • Dla obiektu Inci dent zostały przydzielone dodatkowe zasoby
• Obiekty Resource otrzymały informację o nowym przydziale
8
F i e l d O f f i c e r reagujący na Incident otrzymał informację
o przydziale zasobów

Wym aga n i a jakościo we W dowolnym punkcie przepływu zdarzeń do bieżącego przypadku


użycia można dołączyć przypadek użycia Vi ewMap, który zainicjowany
przez dyspozytora (Di s p a t c h e r ) obejmuje wyświetlenie mapy. Mapa
jest automatycznie pozycjonowana w taki sposób, by miejsce zdarzenia
na niej było widoczne dla dyspozytora.

Relacja rozszerzania

Relacja rozszerzania to inny środek redukowania modelu przypadku użycia. Mając


konkretny przypadek użycia, możemy rozszerzyć go o dodatkowe zdarzenia, a uzyskamy
nowy, bardziej obszerny. Relacja rozszerzenia wskazuje, że instancja pewnego przypadku może
zawierać (pod pewnymi warunkami) zachowanie reprezentowane przez przypadek rozsze-
rzający. Typowym zastosowaniem relacji rozszerzania jest specyfikowanie zachowania w sy-
tuacjach wyjątkowych. Jest na przykład zrozumiałe, że połączenie sieciowe między dyspozyto-
rem (Dispatcher) a funkcjonariuszem (Fi el dOf f i cer) może zostać przerwane w dowolnej
chwili z różnych przyczyn (przykładowo gdy FieldOfficer wjedzie swym samochodem do
tunelu). Przypadek użycia ConnectionDown (patrz rysunek 2.15) opisuje zbiór zdarzeń gene-
rowanych przez system i aktorów w sytuacji przerwania połączenia; przypadek ten rozszerza
przypadki Openlncident i Al 1 ocateResources. Oddzielenie zachowania wyjątkowego od
„normalnego" umożliwia tworzenie krótszych i bardziej ukierunkowanych na meritum przy-
padków użycia. W tekstowym opisie przypadku użycia relacja rozszerzania specyfikowana jest
w warunkach wstępnych przypadku rozszerzającego — w diagramie na rysunku 2.15 jest
nim przypadek Connect! onDown (patrz opis w tabeli 2.3).

Rysunek 2.15. Przykład relacji rozszerzania


84 Rozdział 2. • Modelowanie w języku UML

Tabela 2.3. Tekstowa reprezentacja relacji rozszerzania zawierania z rysunku 2.15 (słowo „rozszerza"
wyszczególnione jest czcionką pogrubioną)

Nazwa przypadku użycia ConnectionDown

Aktorzy uczestniczący Di s p a t c h e r i Fi el dOf f i c e r komunikują się ze sobą

Przepływ zdarzeń [...]

Warunki wstępne Ten przypadek rozszerza przypadki użycia Openlncident


i Al 1 ocateResources. Jest inicjowany przez system w momencie,
gdy przerwane zostanie połączenie sieciowe między aktorami
Di s p a t c h e r i Fi el dOf f i cer.

Warunki końcowe [...]

Tym, co odróżnia relacje rozszerzania od relacji zawierania, jest lokalizacja zależności.


Załóżmy, że dla aktora Dispatcher stworzyliśmy kilka nowych przypadków użycia, na przykład
Updatelncident i Real 1 ocateResources. G d y b y ś m y m o d e l o w a l i p r z y p a d e k ConnectionDown
przy użyciu relacji zawierania, autorzy przypadków Updatelncident i Real 1 ocateResources
musieliby być świadomi istnienia przypadku ConnectionDown. Używając relacji rozszerzania,
eliminujemy taką konieczność, bo przypadek ConnectionDown dokonuje sam z siebie roz-
szerzania dwóch pozostałych. Generalnie wszelkie sytuacje wyjątkowe (takie jak błędy, pomoc
czy inne nieoczekiwane warunki) modelowane są przy użyciu relacji rozszerzania, natomiast
przypadki użycia reprezentujące zachowanie współdzielone przez ograniczony zbiór innych
przypadków użycia modelowane są za pomocą relacji zawierania.

Relacja dziedziczenia

Relacja dziedziczenia to trzeci mechanizm redukowania złożoności modelu. W wyniku


dziedziczenia jeden przypadek użycia może stanowić konkretyzację innego, bardziej ogólnego,
przez wzbogacenie go o dodatkowe szczegóły. I tak na przykład aktor Fi el dOf f i cer musi się
zalogować (uwierzytelnić) do systemu FRIEND. We wczesnym stadium zbierania wymagań
uwierzytelnianie jest modelowane przez ogólny („wysokopoziomowy") przypadek użycia
Authenti cate. Później programiści opisują uwierzytelnianie w sposób bardziej szczegółowy,
zależny od konkretnej platformy sprzętowej, przez co powstawać mogą na przykład przy-
padki użycia Authenti cateWithPassword (który odpowiada typowemu logowaniu aktora
FieldOfficer na komputerze pozbawionym specyficznych urządzeń) i AuthenticateWithCard
(który reprezentuje logowanie przy użyciu czytnika kart inteligentnych). Te dwa nowe przy-
padki użycia stanowią konkretyzacje (specjalizacje) przypadku użycia Authenti c a t e (patrz
rysunek 2.16). W tekstowym opisie przypadku użycia takie specjalizowane przypadki dziedziczą
z przypadku ogólnego aktora inicjującego oraz warunki wstępne i końcowe (patrz tabela 2.4).
Zwróćmy uwagę na istotną różnicę między relacją rozszerzania a relacją dziedziczenia.
Każdy z dwóch przypadków użycia uwikłanych w relację rozszerzania opisuje inny przepływ
zdarzeń, podporządkowany innemu zadaniu. Na rysunku 2.15 przypadek Openlncident
opisuje akcje składające się na „pierwszą reakcję" w związku z incydentem, natomiast przypadek
użycia ConnectionDown opisuje działania podejmowane przez system i aktorów w sytuacji
2.4. UML. — głębszy wgląd 85

Authenticate
WithCard

Rysunek 2.16. Przykład relacji dziedziczenia. Przypadek użycia Authenti c a t e jest wysokopoziomowym
przypadkiem opisującym proces uwierzytelniania w kategoriach ogólnych. Przypadki A u t h e n t i c a t e
^-WithPassword i Authenti cateWi thCard są jego specjalizacjami

Tabela 2.4. Tekstowa reprezentacja relacji dziedziczenia z rysunku 2.16

Nazwa przypadku użycia AuthenticateWithCard

Aktorzy uczestniczący Dziedziczeni z przypadku użycia A u t h e n t i c a t i o n


Przepływ zdarzeń 1. Fi el dOff i c e r umieszcza swą kartę w czytniku terminala.

2. Terminal akceptuje kartę i wyświetla k o m u n i k a t


z żądaniem wprowadzenia kodu PIN.
3. Fi el dOf f i c e r wprowadza kod PIN z klawiatury numerycznej.
4. Czytnik terminala dokonuje porównania wprowadzonego
kodu PIN z kodem PIN zapisanym na karcie. Gdy oba
numery są zgodne, Fi el dOf f i cer zostaje uwierzytelniony,
w przeciwnym razie próba uwierzytelniania zostaje
odrzucona.

Warunki wstępne Dziedziczone z przypadku użycia A u t h e n t i c a t i o n

Warunki końcowe Dziedziczone z przypadku użycia A u t h e n t i c a t i o n

zerwania połączenia sieciowego. Na rysunku 2.16 natomiast przypadki Authenti cateWi th


^Password i Authenticate opisują ten sam proces — uwierzytelnianie — tyle że na różnych
poziomach abstrakcji.

Scenariusze

Przypadek użycia jest abstrakcją, która opisuje wszelkie możliwe scenariusze związane
z określonym elementem funkcjonalnym systemu. Scenariusz jest instancją przypadku użycia,
opisującą konkretny ciąg akcji. Scenariusze używane są jako przykłady ilustrujące konkretne
sytuacje — ich treść koncentruje się na zrozumiałości. Przypadki użycia przeznaczone są do
opisywania ogółu sytuacji pewnego rodzaju — sens istnienia przypadków użycia zasadza się
na kompletności. Scenariusze opisywać będziemy w postaci szablonu składającego się z trzech
pól, określających kolejno:

• nazwę scenariusza, umożliwiającą jego jednoznaczne identyfikowanie; nazwa ta


będzie podkreślona dla zaznaczenia, że mamy do czynienia z konkretną instancją,
86 Rozdział 2. • Modelowanie w języku UML

® instancje aktorów uczestniczących w scenariuszu — ich nazwy również będą


podkreślone,
® przepływ zdarzeń, czyli opis, krok po kroku, sekwencji zdarzeń składających się na
scenariusz.

Zauważmy, że dla scenariusza nie określa się warunków wstępnych ani warunków
końcowych. Warunki te są bowiem abstrakcjami umożliwiającymi programistom opisywanie
całego spektrum założeń, których spełnienie jest konieczne do wywołania przypadku użycia.
Ponieważ scenariusz dedykowany jest konkretnej sytuacji, warunki te są zbędne.
W tabeli 2.5 zamieszczamy przykładowy scenariusz odzwierciedlający zdarzenie po-
żaru w hurtowni.

Tabela 2.5. Scenariusz warehouseOnFi re jako instancja przypadku użycia ReportEmergency

Nazwa warehouseOnFi re

Instancje aktorów uczestniczących bob, alice:FieldOfficer


,iohn:Dispatcher
Przepływ zdarzeń 1. John, patrolując główną ulicę, zauważa przez o k n o
s a m o c h o d u d y m wydobywający się z h u r t o w n i . Jego
p a r t n e r k a Alice aktywuje funkcję „Raport Emergency"
na laptopie systemu FRIEND.
2. Alice w p r o w a d z a do f o r m u l a r z a adres b u d y n k u , jego
orientacyjną lokalizację ( p ó ł n o c n o - z a c h o d n i zakątek)
i rozmiar zagrożenia. Dodatkowo żąda kilku jednostek
pomocy paramedycznej, gdyż okolica pożaru jest dość
gęsto zaludniona. Wysyła formularz i czeka na potwierdzenie.

3. Dyspozytor (Di spatcher) John zostaje zaalarmowany


przez sygnał dźwiękowy na swojej stacji roboczej. Odczytuje
informację od Alice i wysyła potwierdzenie raportu.
Aktywuje jednostkę gaśniczą oraz dwie jednostki
paramedyczne i wysyła do Alice informację o szacunkowym
czasie oczekiwania na przybycie (ETA — Estimated
Arrival Time).

4. Alice odczytuje potwierdzenie i informację o ETA.

2.4.2. Diagramy klas

Klasy i obiekty

Diagramy klas opisują strukturę systemu w kategoriach klas i obiektów. Klasa jest
abstrakcją specyfikującą atrybuty i zachowanie zbioru obiektów — składa się na nią kolekcja
wszystkich obiektów współdzielących identyczny zbiór atrybutów, który odróżnia te obiekty
od obiektów należących do innych kolekcji. Obiekt jest encją zamykającą (enkapsulujacą)
w sobie określony stan i zachowanie. Każdy obiekt ma swoją tożsamość — można się do niego
jednoznacznie odwoływać, odróżniając go od innych obiektów.
2.4. UML. — głębszy wgląd 87

W diagramach UML Idasy i obiekty reprezentowane są przez prostokąty, podzielone


w pionie na trzy części. Górna część zawiera nazwę klasy (obiektu), środkowa wyszczególnia
jego atrybuty, zaś w dolnej części prezentowane są operacje klasy (obiektu). Dwie ostatnie
części pomija się niekiedy dla potrzeb czytelności. Nazwy obiektów są podkreślone, co oznacza,
że są one instancjami. Tradycyjnie nazwa klasy rozpoczyna się dużą literą. Obiekt może być
opatrzony nazwą, po której następuje nazwa jego klasy; w takim przypadku nazwa obiektu
rozpoczyna się małą literą. W systemie FRIEND (patrz rysunki 2.17 i 2.18) Bob i Alice są
funkcjonariuszami, a na diagramie klas reprezentowani są przez obiekty (odpowiednio)
b o b : F i e l d O f f i c e r i al i c e : F i e l d O f f i c e r ; F i e l d O f f i c e r jest klasą opisującą wszystkie
obiekty reprezentujące funkcjonariuszy, podczas gdy bob i al i ce są dwoma jej obiektami.

Rysunek 2.17. Przykładowy diagram klas uczestniczących w przypadku użycia ReportEmergency. Szcze-
gółowe informacje na temat typu atrybutów są zwykle pomijane, aż do etapu projektowania obiektów
(patrz rozdział 9. „Projektowanie obiektów: specyfikowanie interfejsów")

Rysunek 2.18. Przykładowy diagram klas: obiekty uczestniczące w scenariuszu warehouseOnFi r e

W diagramie na rysunku 2.17 ldasa FieldOfficer posiada dwa atrybuty: name i badge
^Number, odpowiadające imieniu i numerowi odznaki funkcjonariusza; oznacza to, że atrybuty
te posiadać będzie każdy obiekt klasy Fi el dOff i cer. W diagramie na rysunku 2.18 w obiektach
bob: Fi el dOf f i cer i al i ce: Fi el dOf f i cer atrybuty te mają konkretne wartości, odpowiednio
„Bob D." i „132" oraz „Alice W." i „23". Atrybut FieldOfficer.name jest wielkością typu
S t r i n g , co oznacza że mogą mu być przypisywane jako wartości tylko instancje klasy
String. Generalnie typ atrybutu określa zbiór wartości, jakie atrybut ten może przyjmować.
88 Rozdział 2. • Modelowanie w języku UML

Jeżeli w danym kontekście nie jest istotny typ atrybutu, decyzję o jego ustaleniu odkłada się
na późniejsze stadia projektu. Pozwala to programistom skoncentrować się na funkcjonal-
ności systemu i uwalnia od drobiazgowych zmian każdorazowo, gdy zmienia się obraz tej
funkcjonalności.

Skojarzenia i łącza

Łącze reprezentuje połączenie między dwoma obiektami. Skojarzenie jest relacją mię-
dzy klasami i może być uważane za grupę łączy. Każdy obiekt klasy Fi el dOf f i cer powiązany
jest z listą raportów (stanowiących obiekty klasy EmergencyReport) sporządzonych przez
funkcjonariusza reprezentowanego przez ten obiekt — na rysunku 2.17 linia łącząca klasy
Fi el dOffi cer i EmergencyReport jest skojarzeniem. Linia łącząca obiekty al i ce: Fi el dOff i cer
i report_129ł: EmergencyReport jest łączem. Łącze to reprezentuje w systemie informację
o tym, że funkcjonariusz al i c e : F i e l d O f f i c e r sporządził raport report_1291:Emergency
•-•Report.
Skojarzenia UML mogą być symetryczne (dwukierunkowe) lub asymetryczne (jedno-
kierunkowe). Na rysunku 2.19 widzimy przykład jednokierunkowego skojarzenia między
klasami Wielokąt i Punkt. Strzałka skierowana w stronę pierwszej z tych klas oznacza, że
system realizuje nawigację jedynie z klasy Wi el okąt do klasy Punkt. Innymi słowy, mając dany
konkretny wielokąt, możemy wyliczyć wszystkie punkty będące jego wierzchołkami; dla kon-
kretnego punktu nie sposób jednak wskazać wielokąta, którego wierzchołkiem jest (ewen-
tualnie) ów punkt.

Wielokąt Punkt

Rysunek 2.19. Przykład skojarzenia jednokierunkowego. Programiści zwykle pomijają nawigację na etapie
analizy i określają ją dopiero na etapie projektowania obiektów (patrz rozdziały 8. „Projektowanie
obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych" i 9. „Projektowanie obiektów: spe-
cyfikowanie interfejsów")

Analogicznie skojarzenia dwukierunkowe mogą być reprezentowane przez linie zakoń-


czone strzałkami z obu stron, choć często pomija się wówczas obie strzałki — linia nieskie-
rowana reprezentuje więc skojarzenie dwukierunkowe.

Klasy skojarzeniowe

Skojarzenia podobne są do klas w tym sensie, że można przyporządkowywać im atrybuty


i operacje. Skojarzenie posiadające własne atrybuty i operacje nazywane jest klasą skojarze-
niową. Klasy skojarzeniowe przedstawiane są na diagramach, tak jak „zwykłe" klasy, jednakże
prostokąt symbolizujący klasę skojarzeniową połączony jest z odnośnym skojarzeniem linią
przerywaną. W diagramie na rysunku 2.20 przydzielenie funkcjonariusza (klasa Fi el dOffi cer)
do incydentu (klasa Inci dent) modelowane jest jako klasa skojarzeniowa posiadająca atrybuty
role i notificationTime.
2.4. UML. — głębszy wgląd 89

Rysunek 2.20. Przykład klasy skojarzeniowej

Każdą klasę skojarzeniową wraz z odnośnym skojarzeniem można przekształcić w zwy-


kłą klasę. Klasy uprzednio skojarzone ze sobą przestają być skojarzone bezpośrednio i zostają
skojarzone z nową klasą, czego przykład widzimy na rysunku 2.21. Mimo iż reprezentacja na
tym rysunku podobna jest do tej z rysunku poprzedniego, to jednak wersja z klasą skojarze-
niową jest czytelniejsza: tak jak skojarzenie nie może istnieć bez klas, które kojarzy, tak klasa
Al 1 ocati on z rysunku 2.21 nie ma samoistnej racji bytu, co jednak z diagramu w żaden sposób
nie wynika. I choć oba diagramy (2.20 i 2.21) są sobie równoważne pod względem prezento-
wanej informacji, drugi z nich wymaga bardziej uważnej analizy krotności skojarzeń. Kom-
promisy tego rodzaju rozpatrywać będziemy dokładniej w rozdziale 5. „Analiza wymagań".

Rysunek 2.21. „Zwykła" klasa będąca wynikiem transformacji ldasy skojarzeniowej

Role

Każdy z końców skojarzenia może być etykietowany przez rolę. Na rysunku 2.17 sko-
jarzenie między klasami F i e l d O f f i c e r i EmergencyReport etykietowane jest rolami author
(„autor") i reportsGenerated („utworzone raporty"). Etykietowanie skojarzeń za pomocą ról
nie tylko ułatwia rozróżnienie wielu skojarzeń wychodzących z tej samej klasy, lecz także
sprawia, że znaczenie samych skojarzeń staje się czytelniejsze.

Krotność

Każdy koniec skojarzenia może być także etykietowany za pomocą zbioru nieujemnych
liczb całkowitych. Liczby te określają dopuszczalną liczbę skojarzeń wychodzących z instancji
danej klasy, a zbiór tych liczb nazywany jest krótko krotnością skojarzenia. Na rysunku 2.17
90 Rozdział 2. • Modelowanie w języku UML

skojarzenie po stronie etykietowanej rolą author ma krotność równą 1 — oznacza to, że każdy
raport (EmergencyReport) sporządzany jest przez dokładnie jednego autora funkcjonariusza
(FieldOfficer); innymi słowy, każdy obiekt klasy EmergencyReport połączony jest łączem
z dokładnie jednym obiektem klasy F i e l d O f f i c e r . Dla odmiany, krotność skojarzenia
po stronie etykietowanej jako reportsGenerated ma postać „wiele", wyrażaną przez gwiazd-
kę, która z kolei jest skrótową formą zapisu 0. .n. Ten ostatni zapis wyraża oczywisty fakt,
że dany funkcjonariusz teoretycznie sporządzać może wiele raportów lub nie sporządzić
żadnego.
W języku UML krotność może być dowolnym podzbiorem zbioru nieujemnych liczb
całkowitych — na przykład zbiorem liczb pierwszych, zbiorem liczb parzystych i tak dalej.
W praktyce jednak większość używanych skojarzeń da się podzielić na trzy typy (patrz ry-
sunek 2.22).

Rysunek 2.22. Przykłady różnych krotności skojarzeń — kolejno od góry „jeden do jednego", „jeden na
wiele" i „wiele na wiele"

° Jeden do jednego — takie skojarzenie posiada na obu końcach krotność będącą


liczbą 1. Zgodnie z takim skojarzeniem między klasami (na przykład między klasami
Pol i c e O f f i c e r i BadgeNumber), istnieje dokładnie jedno łącze między instancjami
tych Idas (funkcjonariusz posiada dokładnie jedną odznakę, odznaka identyfikuje
dokładnie jednego funkcjonariusza).
• Jeden na wiele — jeden z końców takiego skojarzenia ma krotność równą 1, drugi
natomiast krotność o postaci 0 . . n lub 1.. n. Skojarzenie takie reprezentuje kompozycję,
na przykład oddział straży pożarnej (ldasa Firellnit) może być właścicielem wielu
wozów strażackich (Fi reTruck), lecz każdy wóz strażacki jest własnością dokładnie
jednego oddziału.
• Wiele na wiele — na każdym z końców takiego skojarzenia krotność ma postać
0 . . n lub 1.. n. Skojarzenie takie występuje na przykład między klasami Fi el dOf f i cer
i Inci dentReport, reprezentującymi (odpowiednio) funkcjonariusza i raport o incy-
dencie: raport taki może mieć wielu autorów, a każdy funkcjonariusz może być
autorem wielu takich raportów. Jest to najbardziej złożony typ skojarzenia.

Określenie krotności skojarzeń zwiększa ilość informacji, jaką możemy uzyskać w za-
kresie dziedziny aplikacyjnej lub dziedziny realizacyjnej. Krotność staje się czynnikiem kry-
tycznym wówczas, gdy określamy liczbę przypadków użycia niezbędnych do manipulowania
2.4. UML. — głębszy wgląd 91

obiektami dziedziny aplikacyjnej. Jakoprzykład rozpatrzmy klasyczny system plików, z kata-


logami (klasa Di rectory) i plikami (klasa Fi l es), traktowanymi ogólnie jako „elementy sys-
temu plików" (klasa abstrakcyjna FileSystemElement). Każdy katalog zawierać może poten-
cjalnie dowolną liczbę takich „elementów". W przypadku systemu ściśle hierarchicznego (jak
stary dobry FAT) każdy „element systemu plików" należy do dokładnie jednego katalogu, co
odpowiada relacji „jeden na wiele" (patrz rysunek 2.23). Jeśli jednak „element systemu plików"
może należeć równocześnie do wielu katalogów — jak w systemach klasy UNIX — agregacja
klasy FileSystemElement z klasą Directories musi być zrealizowana w postaci skojarzenia
„wiele na wiele" (patrz rysunek 2.24).

Rysunek 2.23. Przykład hierarchicznego systemu plików. Katalog (Di r e c t o r y ) może zawierać dowolną
liczbę elementów (FileSystemElement), z których każdy może być plikiem ( F i l e ) lub katalogiem
(Di r e c t o r y ) , lecz każdy taki element jest częścią dokładnie jednego katalogu

Rysunek 2.24. Przykład niehierarchicznego systemu plików. Katalog (Directory) może zawierać do-
wolną liczbę elementów (Fi 1 eSystemEl ement), z których każdy może być plikiem (Fi 1 e) lub katalogiem
(Di r e c t o r y ) , i każdy taki element może być częścią wielu katalogów równocześnie

W tym miejscu czytelnik może odnieść wrażenie, że zagłębiamy się zbytnio w szczegóły,
które istotne bywają raczej w dalszych stadiach realizacji projektu. Otóż niezupełnie: różnice
między hierarchicznym a niehierarchicznym systemem plików mają także swój funkcjonalny
wymiar. Jeśli w systemie możliwe jest, by dany plik był częścią kilku katalogów, musimy
opracować przypadki użycia opisujące między innymi dodawanie pliku do istniejącego już
katalogu (na przykład za pomocą uniksowego polecenia link czy opcji MakeAl i as z menu
kontekstowego pliku w systemie Mac OS X). Ponadto czynność usuwania pliku wymaga opra-
cowania co najmniej dwóch przypadków użycia, obejmujących (odpowiednio) usuwanie
pliku z jednego konkretnego katalogu lub permanentne usuwanie pliku ze wszystkich ka-
talogów, w których jest obecny. Nic w tym dziwnego, że skojarzenia „wiele na wiele" skut-
kować mogą bardziej skomplikowanymi systemami.
92 Rozdział 2. • Modelowanie w języku UML

Agregacja

Skojarzenia wykorzystywane są do reprezentowania szerokiego spektrum połączeń


i zależności między obiektami. Jednym z bardzo często spotykanych w praktyce typów skoja-
rzenia jest agregacja. Przykładowo w polskim ustroju administracyjnym województwo dzieli
się na powiaty, te zaś na gminy, komisariat policji składa się z policjantów, zaś w katalogach
znajdują się pliki. Oczywiście, zależności tego typu mogą być modelowane w postaci skojarzeń
„jeden na wiele", UML oferuje jednak coś więcej — koncepcję agregacji, symbolizowaną przez
zwykłą linię zakończoną rombem po stronie kontenera skojarzenia (czego przykłady widzimy
na rysunkach 2.23 i 2.25). Mimo iż agregacja podobna jest do skojarzenia „jeden na wiele",
nie może być jego zastępnikiem w każdej sytuacji, gdyż posiada specjalną własność, specyficzną
tylko dla niektóiych skojarzeń „jeden na wiele" i „wiele na wiele": reprezentuje hierarchiczny
charakter relacji między encjami. Na rysunku 2.17 klasy Fi el dOff i cer i EmergencyReport
połączone są relacją „jeden na wiele" — funkcjonariusz może być autorem wielu raportów. Jest
jednak oczywiste, że funkcjonariusz nie składa się z raportów, dlatego absolutnie nie mamy tu
do czynienia z agregacją i musimy ograniczyć się do zwykłego skojarzenia.

Województwo O - Powiat O - Gmina

Komisariat O - Policjant

Katalog O - P1 i k

Rysunek 2.25. Przykład agregacji: Województwo składa się z Powi atów, te zaś — z Gmi n; Komi s a r i a t za-
trudnia (czyli składa się z) Pol i cjantów; Katal og może zawierać wiele PI i ków

Kwalifikowanie

Kwalifikowanie to technika redukowania krotności skojarzeń za pomocą kluczy. Skoja-


rzenia typu 1 lub 0. .1 są łatwiejsze do zrozumienia niż skojarzenia typu 0 . . n czy 1 . . n .
Często w ramach skojarzeń typu „jeden na wiele" końcówki po stronie „wiele" odróżniane
są od siebie za pomocą unikalnych nazw. Przykładowo w danym katalogu nie mogą wystę-
pować dwa pliki o tej samej nazwie (choć nic nie przeszkadza dublowaniu nazw plików w ra-
mach różnych katalogów) — każdy plik jest więc jednoznacznie identyfikowany w ramach
katalogu, w którym się znajduje. Nie używając kwalifikowania, wyrażamy ten fakt w postaci
skojarzenia „jeden na wiele", jak w górnej części rysunku 2.26. Możemy jednak zredukować
krotność po stronie „wiele", używając atrybutu f i 1 ename (reprezentującego nazwę pliku) jako
klucza, zwanego także kwalifikatorem — jak w dolnej części rysunku 2.26. Skojarzenie między
klasami Di r e c t o r y i Fi 1 e staje się wówczas skojarzeniem kwalifikowanym.
Redukowanie krotności zawsze jest zjawiskiem pożądanym, bowiem dzięki niemu model
staje się czytelniejszy i zmniejsza się liczba przypadków szczególnych, które trzeba brać pod
uwagę. Programiści powinni zatem dokładnie analizować wszystkie skojarzenia „jeden na
wiele" i „wiele na wiele" pod kątem tego, czy nie dadzą się one redukować przez użycie kwali-
fikatorów będących atrybutami klasy docelowej, tak jak na rysunku 2.26.
2.4. UML. — głębszy wgląd 93

Rysunek 2.26. Przykład redukowania krotności skojarzenia za pomocą kwalifikowania. Dodanie


kwalifikatora sprawia, że diagram staje się bardziej czytelny, i zwiększa ilość prezentowanej przez
niego informacji — w tym przypadku kwalifikowanie odzwierciedla fakt, że nazwa (filename) pliku
(Fi 1 e) jest unikalna w ramach katalogu (Di r e c t o r y )

Dziedziczenie

Dziedziczenie jest relacją wiążącą klasę ogólną z klasami bardziej szczegółowymi, bę-
dącymi jej konkretyzacjami. Dziedziczenie umożliwia opisywanie atrybutów i operacji wspól-
nych dla całego zbioru klas. Przykładowo zarówno funkcjonariusz (Fi el dOffi cer), jak i dys-
pozytor (Di spatcher) posiadają nazwisko (atrybut name) i odznakę z unikalnym numerem
(atrybut badgeNumber), chociaż funkcjonariusz kojarzony jest z raportem o zagrożeniu
(EmergencyReport), zaś dyspozytor kojarzony jest z samym zdarzeniem nadzwyczajnym
(Incident). Oba wspólne atrybuty — name i badgeNumber — stają się wobec tego podstawą
do zdefiniowania bardziej ogólnej klasy, wspólnej dla Fi el dOffi cer i Di spatcher — klasy
PoliceOfficer reprezentującej policjanta w ogóle (patrz rysunek 2.27). Taka ogólna klasa
nazywa się superklasą, zaś dziedziczące po niej klasy bardziej konkretne nazywane są sub-
klasami. Subklasy dziedziczą po swej superklasie wszystkie atrybuty i operacje. Superklasa
może być klasą abstrakcyjną — wyjaśnialiśmy to pojęcie w sekcji 2.3.3 — jej nazwa pisana
jest wówczas kursywą. Taką klasą abstrakcyjną jest na rysunku 2.27 klasa PoliceOfficer.
Klasy abstrakcyjne wykorzystywane są w modelowaniu zorientowanym obiektowo do re-
prezentowania powiązanych ze sobą koncepcji, dzięki czemu zmniejsza się złożoność modeli.

Rysunek 2.27. Przykład dziedziczenia. PoliceOfficer jest klasą abstrakcyjną definiującą wspólne atry-
buty i operacje dla swych konkretnych subklas Fi el dOffi cer i Di s p a t c h e r
94 Rozdział 2. • Modelowanie w języku UML

Zachowanie obiektu opisywane jest przez jego operacje. Jeden obiekt żąda wykonania
określonej operacji przez drugi, wysyłając mu komunikat. Otrzymany komunikat kojarzony
jest z odpowiednią metodą definiowaną w klasie, do której należy obiekt adresat komunikatu,
łub w którejś7 z jej superklas. Metoda jest obiektowym mechanizmem implementowania operacji.
Rozróżnienie między operacją a metodą pozwala na odróżnienie zachowania obiektu
(reprezentowanego przez operację) od jego implementacji (uosabianej przez metodę lub zbiór
metod, definiowanych w różnych klasach na zasadzie dziedziczenia). Na rysunku 2.28 dla klasy
Incident zdefiniowana jest operacja assignResource(), która zainicjowana przez dyspo-
zytora (Dispatcher) powoduje utworzenie skojarzenia między zdarzeniem (obiektem klasy
Incident) a konkretnym zasobem (będącym obiektem klasy Resource). Operacja assignRe-
source() prawdopodobnie przejawia także efekty uboczne, między innymi w postaci „poin-
formowania" przydzielonego zasobu o fakcie i czasie dokonania przydziału. Operacja close()
klasy Incident powoduje zakończenie obsługi wydarzenia reprezentowanego przez obiekt tej
klasy. Mimo iż język UML odróżnia operacje od metod, programiści często ignorują tę różnicę,
mówiąc po prostu o „metodach".

Rysunek 2.28. Przykład operacji definiowanych w klasie I n c i d e n t

Zastosowania diagramów klas

Diagramy Idas używane są do opisywania struktury systemu. Na etapie analizy pro-


gramiści i projektanci tworzą diagramy klas w celu sformalizowania wiedzy pochodzącej
z dziedziny aplikacyjnej. Klasy reprezentują wówczas obiekty uczestniczące w diagramach
przypadków użycia oraz diagramach interakcji i opisują atrybuty i operacje tych obiektów.
Zadaniem modeli analitycznych jest opisanie zakresu tematycznego systemu i zidentyfikowa-
nie jego granic (warunków brzegowych). W przykładzie z rysunku 2.17 analityk potrafi odpo-
wiednio zinterpretować krotności skojarzenia łączącego klasy Fi el dOffi cer i EmergencyReports
(„jeden funkcjonariusz może sporządzić zero lub kilka raportów o zagrożeniu, każdy taki ra-
port jest sporządzany przez dokładnie jednego funkcjonariusza") i poddać tę kwestię pod
ocenę użytkownikowi — czy raport o zagrożeniu może mieć jednak wielu autorów? Czy
dopuszczalne są raporty anonimowe? Zależnie od otrzymanej odpowiedzi analityk być może
zmieni model tak, by faktycznie odzwierciedlał dziedzinę aplikacyjną. Tworzenie modeli ana-
litycznych opisujemy w rozdziale 5. „Analiza wymagań".
Modele analityczne abstrahują od kwestii implementacyjnych. Zagadnienia, takie jak
szczegóły interfejsu użytkownika, komunikacja sieciowa czy sposób magazynowania danych,

7
Dana klasa może mieć kilka superklas nawet w warunkach jednokrotnego dziedziczenia: po prostu re-
lacja „bycia superklasą" jest relacją przechodnią — superklasa superklasy jest superklasą — p r z y p . tłum.
2.4. UML. — głębszy wgląd 95

nie leżą w gestii tych modeli. Dopiero na następnych etapach diagramy klas uzupełniane są
o elementy reprezentujące koncepcje pochodzące z obszaru dziedziny realizacyjnej. Wtedy
właśnie programiści definiują klasy reprezentujące bazy danych, okna interfejsu użytkownika,
otoczki adaptujące gotowy przestarzały kod do nowych potrzeb, optymalizację kodu i tak dalej.
Klasy te grupowane są zwykle w podsystemy z dobrze zdefiniowanymi interfejsami. Budowanie
takich modeli opisujemy w rozdziałach 6. „Projektowanie systemu — dekompozycja na
podsystemy", 8. „Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzor-
cowych", 9. „Projektowanie obiektów: specylikowanie interfejsów" i 10. „Odwzorowywanie
modelu na kod".

2.4.3. Diagramy interakcji


Diagramy komunikacji przedstawiają wzorce komunikacji pomiędzy obiektami z pewnego
zbioru. Obiekty współdziałają ze sobą poprzez wysyłanie komunikatów. Odebranie komuni-
katu przez obiekt wyzwala wykonywanie określonej jego metody, a skutkiem tego wykony-
wania może być wysyłanie kolejnych komunikatów do innych obiektów. Częścią komunikatu
są zazwyczaj argumenty (parametry), które przekazywane są do wspomnianej metody. W ję-
zyku UML diagramy interakcji występują w dwóch odmianach: diagramów sekwencji i dia-
gramów komunikacyjnych.
Diagram sekwencji ma strukturę dwuwymiarową: kierunek poziomy wypełniany jest
przez poszczególne obiekty uczestniczące w interakcji, zaś kierunek pionowy reprezentuje
upływający czas. Wyjaśnimy tę zasadę na przykładzie zegarka posiadającego dwa przyciski,
reprezentowanego przez klasę 2Bwatch. Aby skorygować wskazanie czasu, posiadacz zegarka,
aktor 2Bwatch0wner, naciska równocześnie oba przyciski, przestawiając zegarek w tryb usta-
wiania czasu. W trybie tym można oddzielnie korygować wskazanie każdej z sekcji — godzin,
minut i sekund; aktualnie korygowana sekcja (nazwijmy ją sekcją aktywną) wyróżniona jest
przez migotanie. Bezpośrednio po wejściu w tryb ustawiania czasu aktywna jest sekcja godzin,
a naciskanie pierwszego przycisku powoduje cykliczne przenoszenie aktywności — z godzin
na minuty, z minut na sekundy, z sekund z powrotem na godziny i tak dalej. Naciskanie dru-
giego przycisku powoduje inkrementowanie wskazania aktywnej sekcji (cyklicznie — po
osiągnięciu maksymalnej wartości na przykład 59 dla godzin czy minut następuje wartość 0).
Równoczesne naciśnięcie obu przycisków powoduje ponowne przejście zegarka w tiyb od-
mierzania czasu.
Diagram na rysunku 2.29 odpowiada sytuacji, gdy aktor zwiększa o 1 wskazanie sekcji
minut. Każda kolumna diagramu odpowiada jednemu obiektowi uczestniczącemu w interak-
cji; komunikaty reprezentowane są przez linie ciągłe zakończone wypełnionymi strzałkami,
etykiety opatrujące te linie są nazwami komunikatów, być może zawierających także parame-
try. Aktywacja metody wywoływanej wskutek otrzymania przez obiekt komunikatu repre-
zentowana jest przez pionowe prostokąty. Aktor inicjujący interakcję reprezentowany jest
w skrajnej lewej kolumnie — pochodzące od niego komunikaty reprezentują interakcje
przedstawiane w diagramach przypadków użycia. Jeśli w przypadek użycia uwikłani są także
inni aktorzy, są oni reprezentowani w skrajnych prawych kolumnach i mogą otrzymywać
komunikaty. Mimo iż dla prostoty wszelkie interakcje na diagramie reprezentowane są w spo-
sób jednolity, przez komunikaty, należy mieć świadomość, że interakcja między aktorami
a systemem ma zupełnie inną naturę niż interakcja między obiektami systemu.
96 Rozdział 2. • Modelowanie w języku UML

Rysunek 2.29. Przykład diagramu sekwencji — korygowanie wskazań zegarka 2Bwatch

Diagramy sekwencyjne mogą być wykorzystywane do opisywania sekwencji abstrak-


cyjnych (czyli wszelkich możliwych interakcji) lub konkretnych sekwencji zdarzeń, tak jak na
rysunku 2.29. W tym pierwszym przypadku w diagramie może być konieczne przedsta-
wienie wyboru jednego spośród dwóch komunikatów, na podstawie pewnego warunku i (lub)
iteracji komunikatów — na tę okazję język UML oferuje stosowne elementy notacji (patrz
rysunek 2.30). Iteracja reprezentowana jest przez opatrzenie etykietą 1 oop powtarzanego
fragmentu, zaś wybór (alternatywa) reprezentowany jest przez etykietę al t: po wyszczegól-
nieniu warunku w nawiasach prostokątnych (tu: i>0) następuje komunikat wysyłany przy
spełnieniu tego warunku (tu: op 1 ()), zaś komunikat wysyłany w przeciwnym razie (tu: op2 ())
specyfikowany jest po frazie [el se].

Rysunek 2.30. Przykłady wyboru i iteracji w diagramie sekwencji


2.4. UML. — głębszy wgląd 97

Diagramy komunikacyjne wyrażają tę samą informację, co diagramy sekwencji, lecz


sekwencje komunikatów reprezentowane są przez ponumerowanie interakcji. Z jednej
strony, znosi to geometryczne wymogi formy, niezbędne dla diagramu sekwencji, i skutkuje
diagramem w formie bardziej zwartej, z drugiej jednak, sprawia, iż przesyłane komunikaty
staja się nieco trudniejsze do śledzenia. Na rysunku 2.31 przedstawiamy diagram komunika-
cyjny równoważny diagramowi sekwencji z rysunku 2.29.

Rysunek 2.31. Przykład diagramu komunikacyjnego, przedstawiającego korygowanie wskazań zegarka


2Bwatch. Diagram ten reprezentuje ten sam przypadek użycia, co diagram sekwencji z rysunku 2.29

Zastosowanie diagramów interakcji

Diagramy interakcji opisują interakcje między obiektami. Jednym z powodów kon-


struowania takich diagramów jest identyfikowanie zakresu odpowiedzialności poszczególnych
klas w diagramach klas, a być może także odkrywanie potrzeby definiowania nowych klas.
Innymi słowy, diagramy interakcji pomagają programiście w dokonaniu przyporządkowania
poszczególnych operacji do odpowiednich obiektów. Zwykle dzieje się tak, że dla każdego
przypadku użycia konstruowany jest diagram interakcji koncentrujący się na przepływie
zdarzeń tego przypadku. Programista identyfikuje obiekty uczestniczące w przypadku użycia
i rozdziela między nie poszczególne elementy zachowania opisywanego przez ten przypadek,
definiując stosowne operacje.
Diagramy interakcji występują zwykle w duecie z odpowiednimi diagramami klas: gdy
zdefiniowany zostanie początkowy diagram klas, dla każdej z kolejnych jego mutacji tworzony
jest diagram interakcji. Często prowadzi to do ulepszania samego przypadku użycia przez ko-
rygowanie niejasnych opisów, uzupełnianie niekompletnych opisów zachowań i tym po-
dobne, a w konsekwencji do definiowania nowych obiektów i usług. Do diagramów interakcji
powrócimy w rozdziale 5. „Analiza wymagań".
98 Rozdział 2. • Modelowanie w języku UML

2.4,4. Diagramy stanów


Maszyna stanu to element języka UML opisujący sekwencję stanów, w jakich kolejno prze-
bywa dany obiekt w ramach odpowiedzi na zdarzenia zewnętrzne. Model maszyny stanów
UML jest rozszerzeniem modelu automatu skończonego. Maszyna stanów dostarcza środki
zarówno do zagnieżdżania stanów (stan może być opisywany przez maszynę stanów), jak
i wiązania stanu obiektu z komunikatami oraz warunkami. Maszyna stanów UML bazuje
w dużym stopniu na mapach stanów (statecharts) D. Harela ([Harel, 1987]) zaadaptowanych
do wykorzystywania na gruncie modelu obiektowego [Douglass, 1999], Maszynę stanów UML
wykorzystywać można z powodzeniem do reprezentowania dowolnego automatu Mealyego
lub dowolnego automatu Moore'a.
Poprzez stan obiektu rozumiemy warunek określony przez jego atrybuty. Przykładowo
obiekt klasy Incident systemu FRIEND może znajdować się czterech stanach: Active, Inactive,
Closed i Archived (patrz rysunek 2.32). Stan Active oznacza rozpoznanie sytuacji wymagającej
interwencji (pożar, wypadek drogowy). Skuteczna interwencja prowadzi do stanu Inactive
— sytuację opanowano, ale nie sporządzono jeszcze raportu (na przykład ugaszono pożar,
lecz nie oszacowano jeszcze strat). Gdy skompletowana zostanie dokumentacja incydentu,
będziemy mieli do czynienia ze stanem Cl osed. „Zamknięty" incydent może z czasem zostać
przeniesiony z bazy głównej do archiwum i wtedy zaistnieje stan Archi ved. W ogólnym przy-
padku stan obiektu ustalany jest na podstawie kilku jego atrybutów.

Rysunek 2.32. Diagram stanów dla klasy Inci dent

Przejście między stanami to zmiana stanu wywołana przez zdarzenie, spełnienie wa-
runku lub upływ czasu. Na diagramie z rysunku 2.32 widoczne są trzy przejścia, odpowiednio
o d s t a n u Active d o Inactive, o d Inactive d o Cl osed.i o d Closed d o Archived.
Stan reprezentowany jest na diagramie przez zaokrąglony prostokąt, zaś przejście między
stanami — przez linię ciągłą, zakończoną otwartą strzałką i łączącą prostokąty reprezentujące
owe stany. Każdy stan etykietowany jest przez swą nazwę. Małe czarne kółeczko reprezentuje
stan początkowy, zaś czarne kółeczko otoczone białym.pierścieniem — stan końcowy.
Na rysunku 2.33 widzimy inny przykład — maszynę stanów dla zegarka 2Bwatch (tego
samego, dla którego skonstruowaliśmy diagram sekwencji widoczny na rysunku 2.29). Z per-
spektywy najwyższego poziomu abstrakcji zegarek ten może znajdować się w dwóch stanach:
MeasureTime, który odpowiada pomiarowi czasu, oraz SetTime, który odpowiada korygo-
waniu wskazania czasu. Naciśnięcie równocześnie obu jego przycisków powoduje zmianę
stanu; przy przejściu ze stanu SetTime do stanu MeasureTime zegarek wydaje krótki sygnał
2.4. UML. — głębszy wgląd 99

Rysunek 2.33. Diagram stanów dla funkcji SetTime zegarka 2Bwatch

dźwiękowy, co odpowiada akcji /beep towarzyszącej temu przejściu. Po pierwszym włączeniu


zasilania (czyli po wymianie baterii) zegarek znajduje się w stanie SetTime — dlatego na
diagramie stan ten jest wyróżniony jako początkowy. Gdy wyczerpie się bateria (zdarzenie
batteryEmpty), zegarek staje się bezużyteczny do czasu jej wymiany: stan DeadBattery repre-
zentujący tę sytuację wyróżniony więc został jako końcowy.
W prezentowanym przykładzie przejścia między stanami mogą być wyzwalane przez zda-
rzenia (pressBothButtons lub batteryEmpty) albo przez upływ czasu (mi nęły 2 mi nuty)8.
Na rysunku 2.34 widoczny jest ulepszony diagram stanów dla zegarka 2Bwatch. Powstał
on w wyniku wzbogacenia diagramu z rysunku 2.33 o akcje opisujące zachowanie się obiektu
w ramach poszczególnych stanów. Akcje są fundamentalnymi jednostkami przetwarzania;
mogą czerpać i wykorzystywać informację wejściową oraz produkować informację wynikową,
mogą też zmieniać stan systemu. Akcje wykonywane są zwykle dość szybko i nie są przery-
walne — akcja musi wykonać się w całości lub nie wykonać w ogóle.
W szczególności akcja może być zrealizowana jako wywołanie operacji. W ramach ma-
szyny stanów akcje zachodzić mogą w trzech miejscach:

• w czasie przejścia między stanami (jak akcja beep związana z przejściem od


stanu SetTime do stanu MeasureTime — przejściem wywołanym przez zdarzenie
pressBothButtons),

• w momencie wchodzenia obiektu w określony stan (jak akcja bl ink hours w stanie
SetTime na rysunku 2.34),
• w momencie wychodzenia obiektu z określonego stanu (jak akcja stop bl i nki ng
w stanie SetTime na rysunku 2.34).

8
Gdy zegarek znajduje się w stanie SetTime i przez 2 minuty nie zostanie naciśnięty żaden przycisk,
zegarek samoczynnie przełącza się do stanu MeasureTime — przyp. tłum.
100 Rozdział 2. • Modelowanie w języku UML

Rysunek 2.34. Przejścia między stanami obiektu 2Bwatch

Akcje kończące przebywanie obiektu w danym stanie wykonywane są w pierwszej ko-


lejności. Po nich wykonywane są akcje związane z przejściem między stanami, po czym przy-
chodzi kolej na akcje rozpoczynające przebywanie obiektu w nowym stanie. Akcje związane
z wchodzeniem w dany stan lub wychodzeniem z niego wykonywane są zawsze, bez względu
na przejście odpowiedzialne za zmianę stanu.
Przejściem wewnętrznym jest przejście, które nie prowadzi do zmiany stanu. Przejścia
wewnętrzne wyzwalane są przez zdarzenia i można przypisywać im akcje; uruchomienie
przejścia wewnętrznego nie powoduje natomiast żadnych akcji związanych ze stanem — me
jest wykonywana ani początkowa, ani końcowa akcja dla stanu, w którym obiekt wciąż po-
zostaje. Stan SetTime z diagramu na rysunku 2.34 posiada dwa przejścia wewnętrzne, każde
związane z naciśnięciem jednego przycisku.
Aktywność to skoordynowany zbiór akcji. Ze stanem może być powiązana aktywność
trwająca tak długo, jak długo obiekt pozostaje w tym stanie. Podczas gdy akcje są z założenia
krótkie i nieprzerywalne, aktywność może zajmować dużo czasu i jest przerywana w momen-
cie, gdy uruchamiane jest przejście prowadzące do opuszczenia danego stanu. Na diagramie
aktywności wiązane są ze stanem za pomocą etykiety do/, po której następuje nazwa odnośnej
aktywności. W diagramie na rysunku 2.34 aktywność count t i c k s powiązana jest ze stanem
MeasureTime.
Zagnieżdżone maszyny stanów są kolejnym środkiem redukowania złożoności modeli.
Można ich używać zamiast przejść wewnętrznych. Na rysunku 2.35 bieżące wskazanie czasu
(i daty) traktowane jest jako zagnieżdżony stan, podczas gdy akcje związane z jego modyfi-
kowaniem modelowane są za pomocą przejść wewnętrznych. Zauważmy, że każdy stan może
być modelowany jako zagnieżdżony — przykładowo na stan BlinkHours składają się 24
różne podstany odpowiadające różnych wskazaniom godziny; przejścia między tymi pod-
stanami odbywają się wskutek naciskania prawego przycisku.
2.4. UML. — głębszy wgląd 101

Rysunek 2.35. Udoskonalona maszyna stanów skojarzona ze stanem SetTime. Akcje 1B i rB reprezentują
naciskanie (odpowiednio) lewego i prawego przycisku

Zastosowania diagramów stanów

Diagramy stanów znajdują zastosowanie w reprezentowaniu niebanalnego zachowa-


nia podsystemu lub obiektu. W odróżnieniu od diagramów interakcji skupiających się na
zdarzeniach wpływających na zachowanie grupy obiektów, diagramy stanów uwidaczniają
atrybuty lub grupy atrybutów decydujące o zachowaniu pojedynczego obiektu. Maszyny stanów
używane są także do identyfikowania atrybutów obiektów i ulepszania opisu ich zachowania,
podczas gdy diagramy interakcji umożliwiają identyfikowanie obiektów uczestniczących
w oferowanych usługach. Ponadto diagramy stanów mogą być wykorzystywane na etapie pro-
jektowania systemu i projektowania obiektów do opisu obiektów dziedziny realizacyjnej
przejawiających interesujące zachowania. Diagramami stanów zajmiemy się dokładniej w roz-
działach 5. „Analiza wymagań" i 6. „Projektowanie systemu — dekompozycja na podsystemy".

2.4.5. Diagramy aktywności


Diagramy aktywności reprezentują sekwencjonowanie i koordynowanie zachowania niż-
szego poziomu. Zadaniem diagramu aktywności jest ukazanie, jak zachowanie systemu re-
alizowane jest w postaci jednej lub kilku sekwencji aktywności, przy udziale obiektów ko-
ordynujących te sekwencje. Diagramy aktywności mają naturę hierarchiczną: aktywność może
być pojedynczą akcją, ale może także stanowić graf prostszych podaktywności wraz z powiąza-
nymi obiektami. Diagram aktywności z rysunku 2.36 jest odpowiednikiem diagramu stanów
z rysunku 2.32. Zaokrąglone prostokąty reprezentują akcje i aktywności, zaś linie łączące te pro-
stokąty — przepływ sterowania. Każda aktywność może być rozpoczęta tylko wówczas, gdy
zakończy się wykonywanie wszystkich aktywności poprzedzających ją na diagramie.
Węzły kontrolne stanowią punkty koordynacji przepływu sterowania na diagramie,
dostarczają środki do realizowania współbieżności, synchronizacji i podejmowania decyzji.
Decyzje oznaczają rozgałęzienie przepływu sterowania. Reprezentują wybór jednej
z kilku ścieżek sterowania na podstawie warunku określonego przez stan obiektu lub grupy
obiektów. Na diagramach decyzja oznaczana jest przez romb, do którego prowadzi jedna lub
102 Rozdział 2. • Modelowanie w języku UML

Rysunek 2.36. Diagram aktywności dla klasy Incident. W ramach akcji Handl elnci dent dyspozytor
otrzymuje raporty i przydziela zasoby. Gdy obsługa incydentu zostanie zamknięta, sporządzana jest
jego dokumentacja — w aktywności Documentlncident uczestniczą funkcjonariusze (FieldOfficer)
i dyspozytor (Dispatcher). Gdy incydent przestanie być już tematem nr 1, przenosi się (aktywność
Archivelncident) jego dokumentację do archiwum, zrealizowanego w oparciu o nośnik zapewniający
bardziej efektywne magazynowanie dużej porcji informacji za cenę mniej sprawnego wyszukiwania
(na przykład streamer)

więcej ścieżek wejściowych i z którego wychodzą dwie lub więcej ścieżek wyjściowych. Każda
ścieżka wyjściowa etykietowana jest przez warunek decydujący o jej wyborze. Liczba możli-
wych wartości warunku selekcyjnego determinuje liczbę ścieżek wyjściowych. Na rysunku 2.37
widzimy sytuację, gdy po zakończeniu aktywności Openlncident podejmowana jest decyzja
wyboru jednego z trzech wariantów sterowania: jeśli incydent jest pożarem (fi re) i ma wysoki
priorytet (hi ghPri ori ty), alarmowany jest komendant straży pożarnej; jeśli incydent ma wy-
soki priorytet, lecz jest zdarzeniem innym niż pożar (not fi re), alarmowany jest szef lokalnej
policji. Gdy incydent jest zdarzeniem o niskim priorytecie (1 owPri ori ty), nie ma potrzeby
alarmowania osób na wyższym szczeblu zarządzania, od razu przechodzi się do aktywności
przydzielania zasobów.

Rysunek 2.37. Przykład podejmowania decyzji, zależnie od rodzaju incydentu. Rozróżniane są trzy
wartości warunku selekcyjnego: pożar o wysokim priorytecie ( f i re & hi ghPri ori ty), zdarzenie o wysokim
priorytecie inne niż pożar (not f i r e & hi ghPri ori ty) i zdarzenie o niskim priorytecie (1 owPri ori ty)

Ze współbieżnością wiążą się dwa rodzaje węzłów kontrolnych: rozwidlenia i złączenia.


Rozwidlenie oznacza podział sterowania na dwa lub więcej wątków realizowanych równocześnie,
zaś złączenie oznacza punkt synchronizacji wątków, czyli ponowne połączenie sterowania
w jeden wątek. W diagramie na rysunku 2.38 akcje Ali ocateResources, CoordinateResources
i Document Inci dent mogą być wykonywane współbieżnie, jednakże żadna z nich nie może
się rozpocząć przed zakończeniem akcji Openlncident i wszystkie muszą się zakończyć, by
mogła rozpocząć się akcja Archi vel nci dent.
2.4. UML. — głębszy wgląd 103

Rysunek 2.38. Przykład rozwidlenia i złączenia

Aktywności mogą być grupowane w partycje aktywności, w oryginalnej specyfikacji


UML nazywane obrazowo „torami pływackimi" (swimlanes), w celu ukazania obiektu lub
podsystemu odpowiedzialnego za ich implementację. Partycje takie reprezentowane są na
diagramach aktywności w postaci prostokątów zamykających grupy aktywności; granice
tych partycji mogą być przecinane przez linie reprezentujące przejścia między stanami. Na
rysunku 2.39 widzimy dwie partycje grupujące aktywności związane z obiektami klasy (od-
powiednio) Fi el dOf f i cer i Di spatcher.

Rysunek 2.39. Przykład partycjonowania aktywności

Zastosowania diagramów aktywności

Diagramy aktywności dostarczają spojrzenia na grupę obiektów z perspektywy reali-


zowanych przez nie zadań. Ten punkt widzenia może być użyteczny na przykład przy identy-
fikowaniu sekwencji ograniczających przypadki użycia, sekwencji zachowań przejawianych
przez grupy obiektów czy też przy spojrzeniu na projekt pod kątem realizowanych w ramach
niego zadań. W tej książce wykorzystywać będziemy diagramy aktywności w rozdziałach
14. „Zarządzanie projektem" i 15. „Cykl życiowy oprogramowania".
104 Rozdział 2. • Modelowanie w języku UML

2.4.6. Organizacja diagramów


Modele skomplikowanych systemów rozwijane są sukcesywnie i szybko stają się równie
skomplikowane jak modelowana rzeczywistość. Komplikację tę można zmniejszać, organizując
w pakiety powiązane ze sobą elementy modelu — przypadki użycia, klasy i tym podobne.
Pakiety reprezentowane są na diagramach w postaci prostokątów opatrzonych zakładkami
z lewej strony górnej krawędzi. Diagram z rysunku 2.40 przedstawia system FRIEND pogrupo-
wany według powiązanych z nim aktorów — i tak przypadki użycia związane z zarządzaniem
incydentem (rozpoczynanie interwencji, przydzielanie zasobów, dokumentowanie) zgrupowane
są w pakiecie IncidentManagement, zaś powiązane z archiwizowaniem incydentu (przeno-
szenie danych na nośnik archiwizacyjny, generowanie raportów na podstawie zarchiwizo-
wanych incydentów) zgrupowane są w pakiecie Inci dentArchi ve; pakiet SysAdmi ni s t r a t i on
zawiera natomiast przypadki użycia o charakterze administracyjnym (definiowanie użyt-
kowników czy rejestrowanie zdalnych stacji). Umożliwia to klientowi oraz programistom
powiązanie przypadków użycia w grupy i skupienie się na ich podzbiorze interesującym
w danym kontekście.

Rysunek 2.40. Przykład grupowania przypadków użycia w pakiety — organizacja systemu FRIEND
z perspektywy udziału aktorów

Na rysunkach 2.41 i 2.42 przedstawiamy przykłady użycia pakietów w diagramach klas


— klasy związane z przypadkiem użycia ReportEmergency zorganizowane są pod kątem miej-
sca tworzenia obiektów: klasy Fi el dOf f i cer i EmergencyReport są częścią pakietu Fi el dStati on,
zaś klasy Dispatcher i Incident stanowią część pakietu DispatcherStation. Na rysunku
2.40 widoczne są pakiety grupujące elementy modelu, zaś na rysunku 2.41 przedstawiono
te same pakiety bez wyszczególniania ich zawartości. Rysunek 2.41 prezentuje więc bardziej
2.4. UML. — głębszy wgląd 105

Rysunek 2.41. Kolejny przykład pakietów — są to pakiety z rysunku 2.40, bez uwidaczniania ich zawartości

Rysunek 2.42. Przykład grupowania klas w pakiety: klasy F i e l d O f f i c e r i EmergencyReport są częścią


pakietu F i e l d S t a t i o n , zaś klasy Di s p a t c h e r i I n c i d e n t stanowią część pakietu Di s p a t c h e r S t a t i o n

wysokopoziomowe spojrzenie na system FRIEND, użyteczne przy jego funkcjonalnej analizie,


podczas gdy szczegółowy widok z rysunku 2.40 może być bardziej przydatny podczas dys-
kutowania zawartości poszczególnych pakietów.
Pakiety przyczyniają się do upraszczania diagramów UML na tej samej zasadzie,
zgodnie z którą system plików staje się prostszy dzięki zorganizowaniu plików w katalogi,
z jedną wszakże istotną różnicą: pakiety niekoniecznie muszą mieć strukturę hierarchiczną
— ta sama klasa może pojawić się w kilku pakietach. By zminimalizować ryzyko związanej
z tym faktem niespójności, każda z klas (i ogólnie — każdy element modelu) jest własnością
dokładnie jednego ze wspomnianych pakietów, podczas gdy pozostałe zawierają jedynie
odwołania do niej.
Zauważmy, że pakiety są jednostkami organizacji, nie obiektami: nie można im przypisać
określonego zachowania, nie mogą więc wysyłać i otrzymywać komunikatów.
Notatka jest komentarzem załączonym do diagramu. Notatki używane są przez pro-
gramistów w charakterze informacji uzupełniających model i jego elementy, stanowią więc
idealne narzędzie do zapisywania rozmaitych uwag, wyjaśniania różnych skomplikowanych
kwestii czy tworzenia list problemów czekających na rozwiązanie. Mimo iż notatki pisane
są językiem potocznym i jako takie nie prezentują żadnej określonej semantyki, same z siebie
mogą być nieocenione pod względem formułowania treści niedających się w żaden sposób
wyrazić za pomocą semantyki UML. Widać to wyraźnie choćby na rysunku 2.43, gdzie
notatka w wyraźny sposób artykułuje relacje między klasą EmergencyReport a obydwoma
pakietami.
106 Rozdział 2. • Modelowanie w języku UML

Rysunek 2.43. Przykład notatki. Notatka może być związana z konkretnym elementem diagramu

2.4.7. Rozszerzenia diagramów


Intencją twórców języka UML było dostarczenie notacji wystarczającej do opisywania jak
najszerszej gamy różnych systemów i konstrukcji programistycznych. Nikt nie jest jednak
jasnowidzem i autorzy języka świetnie zdawali sobie sprawę z tego, że nawet najdalej po-
sunięta wyobraźnia nie jest w stanie antycypować wszelkich przyszłych potrzeb w zakresie
modelowania dziedziny aplikacyjnej i dziedziny realizacyjnej. Wprowadzono więc do ję-
zyka UML kilka mechanizmów umożliwiających jego rozszerzanie, stosownie do bieżących
potrzeb konstruktorów modeli. W tej sekcji opiszemy dwa spośród tych rozszerzeń: stereotypy
i ograniczenia.
Stereotyp jest mechanizmem umożliwiającym klasyfikowanie elementów modeli. Na
diagramie reprezentowany jest przez łańcuch znaków zamknięty w poziome znaki cytowania
(guillemets), czyli « oraz » i dołączony do klasyfikowanego elementu. Formalnie dołączenie
stereotypu do elementu modelu jest semantycznie równoważne utworzeniu nowej klasy
w metamodelu UML (czyli w modelu reprezentującym konstrukcję języka UML). Umożliwia
to definiowanie nowych rodzajów bloków konstrukcyjnych, stosownych dla modelu okre-
ślonej domeny. Przykładowo na etapie analizy obiekty modelu dzieli się na trzy kategorie:
obiektów encji {entity), obiektów brzegowych (boundary) i obiektów sterujących (control).
Obiekty każdej z tych kategorii posiadają tę samą strukturę — atrybuty, operacje, skojarzenia
— lecz służą różnym celom. „Czysty" język UML zna tylko jedną kategorię obiektu; by roz-
różniać trzy wymienione kategorie, definiujemy stereotypy «enti ty», «boundary» i «control»,
których przykłady zastosowań widoczne są na rysunku 2.44. Stereotypy te opisujemy szcze-
gółowo w rozdziale 5. „Analiza wymagań". Innym obszarem zastosowania stereotypów mogą
być relacje między obiektami, czego przykład widzieliśmy na rysunkach 2.14 i 2.15 w postaci
stereotypów «i ncl ude» i «extent».

Rysunek 2.44. Przykłady zastosowań stereotypów «enti ty», «boundary» i «control»


2.5. Literatura uzupełniająca 107

Ograniczenie to dodatkowa reguła narzucona na semantykę obiektu, czyniąca tę se-


mantykę bardziej restrykcyjną. Restrykcje te mogą być tego rodzaju, że ich wyrażenie za po-
mocą klasycznej semantyki UML może okazać się wręcz niemożliwe. Powracając do systemu
FRIEND: z określonym incydentem (reprezentowanym przez obiekt klasy Incident) może być
skojarzonych wiele raportów (reprezentowanych przez obiekty klasy EmergencyReport),
co czytelnie daje się wyrazić za pomocą skojarzenia „jeden na wiele". Dyspozytor (Di spatcher)
powinien mieć przy tym szybki dostęp do żądanego dokumentu, a to zapewnić może sobie
jedynie przez uporządkowanie poszczególnych raportów w sposób chronologiczny, czyli
według daty sporządzenia. Język UML nie udostępnia środków umożliwiających określenie
kryterium uporządkowania obiektów po stronie „wiele", lecz brak ten można wypełnić, opa-
trując skojarzenie odpowiednim ograniczeniem, jak na rysunku 2.45. Ograniczenie może być
wyartykułowane bądź w formie potocznej (jak na rysunku), bądź też w specjalnym języku
służącym do tego celu, na przykład w języku OCL (Object Constraint Language, [OMG, 2009]).
Ograniczeniami i językiem OCL zajmiemy się dokładniej w rozdziale 9. „Projektowanie obiek-
tów: specyfikowanie interfejsów".

Rysunek 2.45. Przykład ograniczenia narzuconego na skojarzenie

2.5. Literatura uzupełniająca


Geneza notacji wykorzystywanej na potrzeby modelowania sięga czasów analizy struktu-
ralnej opisanej przez T. De Marca [De Marco, 1978] i projektowania strukturalnego omówio-
nego przez E. Yourdona i L. Constantina ([Yourdon i Constantine, 1975]) — obie te koncepcje,
bazujące na dekompozycji funkcjonalnej, stały się punktem wyjścia dla diagramów prze-
pływu danych, które opisał T. De Marco [De Marco, 1978], bardzo istotnych dla programistów
zajmujących się konserwacją i rozwijaniem starszych aplikacji zaprojektowanych przy udziale
modeli analizy strukturalnej.
Język UML narodził się jako owoc doświadczeń wielu badaczy i praktyków — nazwiska
niektórych z nich wymienialiśmy wcześniej w tym rozdziale. Wysiłek Boocha, Jacobsona
i Rumbaugha doprowadził do powstania jednolitej, szeroko akceptowanej notacji; wcześniej-
sze ich prace, czyli G. Boocha [Booch, 1994], I. Jacobsona, M. Christersona, P. Jonssona
i G. Overgaarda [Jacobson i in., 1992] oraz J. Rumbaugha, M. Błahy, W. Premerlaniego,
F. Eddy'ego i W. Lorensena [Rumbaugh i in., 1991], zawierają głęboki wgląd w naturę analizy
i projektowania zorientowanych obiektowo i wciąż stanowią bogate źródło wiedzy na temat
podstawowych koncepcji obiektowo zorientowanego modelowania.
Ponieważ język UML został zaprojektowany z myślą o szeroldej ldasie zastosowań, jest
standardem dość skomplikowanym. W tym rozdziale skupiliśmy się jedynie na jego podstawo-
wych elementach, wystarczających do rozpoczęcia lektury następnych rozdziałów. Czytelnikom
zainteresowanym bardziej szczegółową wiedzą na temat UML polecamy następujące książki.
108 Rozdział 2. • Modelowanie w języku UML

• Książka M. Fiowera [Fowler, 2003] to krótkie wprowadzenie do UML ilustrowane


wieloma przykładami. Idealne wprowadzenie dla początkującego czytelnika, nie
znającego w ogóle języka UML.
• Książka G. Boocha, J. Rumbaugha i I. Jacobsona [Booch i in., 2005] jest wszechstronną
prezentacją UML w wykonaniu jego głównych autorów. Bardziej obszerna niż dzieło
M. Fiowera [Fowler, 2003], zawierająca mniej przykładów i z tego względu adresowana
raczej dla czytelników bardziej zaawansowanych w modelowaniu.
• Praca OMG Unified Modeling Language Superstructure. Version 2.2 [OMG, 2009] jest
oficjalną specyfikacją UML. Utrzymywana na bieżąco przez gremium rewizyjne, od-
powiedzialne za wyjaśnianie niejednoznaczności, poprawianie błędów i eliminowanie
niespójności na forum społeczności UML.

2.6. Ćwiczenia
2.1. Traktując bankomat jako system, wymień co najmniej trzech współdziałających z nim
aktorów.
2.2. Czy system istniejący jedynie w zamyśle projektantów może być reprezentowany
przez aktora? Uzasadnij odpowiedź.
2.3. Jaka jest różnica między scenariuszem a przypadkiem użycia? Kiedy używa się
każdej z tych konstrukcji?
2.4. Narysuj diagram przypadków użycia automatu do sprzedaży biletów. System za-
wiera dwóch aktorów: podróżnego kupującego bilet określonego rodzaju i cen-
tralny komputer utrzymujący bazę danych o taryfach i cenniku. Przypadki użycia
powinny obejmować zakup zwykłego biletu (BuyOneWayTicket), zakup karnetu
tygodniowego lub miesięcznego (BuyWeeklyCard i BuyMonthlyCard) oraz aktu-
alizację informacji taryfowej o cenach (UpdateTari ff). Nie zapomnij o uwzględ-
nieniu przypadków wyjątkowych, takich jak przeterminowanie transakcji (Timeout
— podróżny po zainicjowaniu transakcji wykazuje zbyt długą bezczynność), anulo-
wanie transakcji (Transacti onAborted — podróżny zainicjował transakcję, lecz
wycofał się z zamiaru kupna biletu, naciskając odpowiedni przycisk), brak nomi-
nałów do prawidłowego wydania reszty (Di s t r i butorOutOfChange) i wyczerpanie
papieru w drukarce (Di s t r i butorOutOfPaper).
2.5. Opisz przepływa zdarzeń i określ wszystkie elementy przypadku użycia Update
"-•Tari f f z ćwiczenia 2.4. Nie zapomnij o relacjach między obiektami.
2.6. Narysuj diagram klas reprezentujący książkę zdefiniowaną następująco: „Książka
podzielona jest na kilka części, z których każda składa się z pewnej liczby rozdziałów.
Rozdziały podzielone są na sekcje". Zwróć szczególną uwagę na klasy i skojarzenia
między nimi.
2.7. Uzupełnij skojarzenia z ćwiczenia 2.6 o krotności.
2.8. Narysuj diagram zawierający obiekty reprezentujące pierwszą część tej książki
„Zaczynamy". Upewnij się, że stworzony przez Ciebie diagram jest spójny z dia-
gramem z ćwiczenia 2.6.
Bibliografia 109

2.9. Rozszerz diagram klas utworzony w ćwiczeniu 2.6 o atrybuty określające:

• wydawcę, datę wydania i numer ISBN książki,


• numer i tytuł każdej części,
® numer, tytuł i streszczenie każdego rozdziału,
• numer i tytuł każdej sekcji.

2.10. Zauważ, że klasy diagramu z ćwiczenia 2.9, reprezentujące część, rozdział i sekcję,
zawierają wspólne atrybuty — tytuł i numer. Dodaj do diagramu abstrakcyjną
superklasę dla tych klas, definiującą wspomniane atrybuty.
2.11. Narysuj diagram klas reprezentujący relację między rodzicami a dziećmi. Pamiętaj,
że człowiek może być zarówno dzieckiem, jak i jednym z rodziców. Zaetykietuj
skojarzenia ich rolami i krotnościami.
2.12. Narysuj diagram klas reprezentujący bibliografię i odwołania do jej pozycji. Twój
diagram powinien być jak najbardziej szczegółowy. Przetestuj go na podstawie
dodatku C do tej książki.
2.13. Narysuj diagram sekwencji dla scenariusza warehouseOnFi re z tabeli 2.5, uwzględ-
niając obiekty bob, al ice, john i FRIEND oraz ewentualne instancje innych klas.
Ogranicz się do pierwszych pięciu przesyłanych komunikatów.
2.14. Narysuj diagram sekwencji dla przypadku użycia ReportEmergency z tabeli 2.1.
Ogranicz się do pierwszych pięciu przesyłanych komunikatów. Upewnij się, że
stworzony przez Ciebie diagram jest spójny z diagramem z ćwiczenia 2.13.
2.15. Przeanalizuj proces zamawiania pizzy przez telefon. Narysuj diagram aktywności
reprezentujący każdy krok tego procesu, od momentu sięgnięcia po telefon do mo-
mentu rozpoczęcia konsumpcji dostarczonej pizzy. Dla uproszczenia pomiń możliwe
sytuacje wyjątkowe. Uwzględnij również aktywności innych obiektów uwikłanych
w proces.
2.16. Dodaj obsługę możliwych sytuacji wyjątkowych do diagramu utworzonego w ćwi-
czeniu 2.15 — uwzględnij co najmniej trzy (na przykład dostarczenie pizzy pod
niewłaściwy adres, dostarczenie niewłaściwego rodzaju pizzy, dostawca powyjadał po
drodze co smakowitsze anchovis i tym podobne).
2.17. Narysuj diagram dla aktywności opisanych w sekcji 1.4 poprzedniego rozdziału, za-
kładając ich ściśle sekwencyjne wykonanie. Narysuj następny diagram, przy założeniu
wykonywania wspomnianych aktywności w sposób przyrostowy (jedna partia syste-
mu jest analizowana, projektowana, implementowana i wyczerpująco testowana
przed rozpoczęciem prac nad następną partią). Wreszcie, na trzecim diagramie
załóż wykonywanie tychże aktywności w sposób równoległy.

Bibliografia
[Booch, 1994] G. Booch Object-Oriented Analysis and Design with Applications,
wyd. drugie, Benjamin/Cummings, Redwood City, CA, 1994.
[Booch i in., 2005] G. Booch, J. Rumbaugh, I. Jacobson The Unified Modeling
Language User Guide, Addison-Wesley, Reading, MA, 2005.
110 Rozdział 2. • Modelowanie w języku UML

[Coad i in., 1995] P. Coad, D. North, M. Mayfield Object Models: Strategies, Patterns,
& Applications, Prentice Hall, Englewood Cliffs, NJ, 1995.
[Constantine i Lockwood, 2001] L. L. Constantine & L.A.D. Lockwood „Structure and style in use
cases for user interface design," w: M. van Harmelen (red.) Object-
Oriented User Interface Design, 2001.

[De Marco, 1978] T. De Marco Structured Analysis and System Specification,


Yourdon, New York, 1978.

[Douglass, 1999] B. P. Douglass Doing Hard Time: Using Object Oriented Programming
and Software Patterns in Real Time Applications, Addison-Wesley,
Reading, MA, 1999.
[Fowler, 2003] M. Fowler UML Distilled: A Brief Guide To The Standard Object
Modeling Language, wyd. trzecie, Addison-Wesley, Reading, MA, 2003.
[Harel, 1987] D. Harel Statecharts: A visual formalism for complex systems,
„Science of Computer Programming", str. 231 - 274, 1987.
[Jacobson i in., 1992] I. Jacobson, M. Christerson, P. Jonsson, G. Overgaard Object-Oriented
Software Engineering — A Use Case Driven Approach, Addison-Wesley,
Reading, MA, 1992.
[Martin i Odell, 1992] J. Martin, J. J. Odell Object-Oriented Analysis and Design, Prentice
Hall, Englewood Cliffs, NJ, 1992.
[Mellor i Shlaer, 1998] S. Mellor, S. Shlaer Recursive Design Approach, Prentice Hall,
Upper Saddle River, NJ, 1998.
[Miller, 1956] G. A. Miller The magical number seven, plus or minus two: Some limits
on our capacity for processing information, „Psychological Review",
t. 63, str. 81 - 97, 1956.
[OMG, 2009] Object M a n a g e m e n t G r o u p OMG Unified Modeling Language
Superstructure. Version 2.2, http://www.omg.org.
[Popper, 1992] K. Popper Objective Knowledge: An Evolutionary Approach, Clarendon,
Oxford, 1992.
[Rumbaugh i in., 1991] J. R u m b a u g h , M. Błaha, W . Premerlani, F. Eddy, W . Lorensen
Object-Oriented Modeling and Design, Prentice Hall, Englewood
Cliffs, NJ, 1991.

[Spivey, 1992] J. M. Spivey The Z Notation, A Reference Manual, wyd. drugie,


Prentice Hall International, Hertfordshire, U.K., 1992.

[Wirfs-Brock i in., 1990] R. Wirfs-Brock, B. Wilkerson, L. Wiener Designing Object-Oriented


Software, Prentice Hall, Englewood Cliffs, NJ, 1990.
[Yourdon i Constantine, 1975] E. Y o u r d o n , L. C o n s t a n t i n e Structured Design, Prentice Hall,
Englewood Cliffs, NJ, 1975.
3.1. Wstęp — katastrofa Ariane 114

3.2. O projekcie ogólnie 115

3.3. Koncepcje organizacyjne projektu 119


3.3.1. Organizacja projektów 119
3.3.2. Role w realizacji projektu 122
3.3.3. Zadania i produkty 124
3.3.4. Harmonogramy 126
3.4. Koncepcje komunikacyjne projektu 128
3.4.1. Komunikacja planowa 128
3.4.2. Komunikacja pozaplanowa 135
3.4.3. Mechanizmy komunikacyjne 138

3.5. Aktywności organizacyjne 146


3.5.1. Dołączanie do zespołu 146
3.5.2. Dołączanie do infrastruktury komunikacyjnej 146
3.5.3. Udział w zebraniach zespołu 147
3.5.4. Organizacja przeglądów 149

3.6. Literatura uzupełniająca 151

3.7. Ćwiczenia 152

Bibliografia 153
Organizacja projektu
i komunikacja
Dwa podzespoły rakiety, dostarczone przez dwóch różnych
podwykonawców, połączone były parą przewodów. Kontrola
przedstartowa wykazała, że połączenie jest nieprawidłowe —przewody
są skrzyżowane i należy zamienić ich kolejność po jednej ze stron.
Śledztwo prowadzone w związku ze spłonięciem rakiety na stanowisku
startowym wykazało, że obaj podwykonawcy sumiennie zastosowali
się do zalecenia — każdy zamienił końcówki po swojej stronie.

Inżynieria oprogramowania jest działalnością zespołową, obejmującą specjalistów z różnych


dziedzin — ekspertów z dziedziny aplikacyjnej, analityków, projektantów, programistów, me-
nedżerów, dokumentalistów technicznych, grafików i użytkowników. Żaden z nich nie jest
w stanie ogarnąć w pojedynkę wszystkich aspektów tworzonego systemu, wszyscy są więc
nawzajem zależni od siebie w swej pracy. Co więcej, wszelkie zmiany w zakresie dziedziny
aplikacyjnej czy dziedziny realizacyjnej wymagają także zmian w rozumieniu systemu przez
poszczególnych uczestników. W tych warunkach krytycznym czynnikiem uzależniającym
powodzenie całego przedsięwzięcia jest skuteczna wymiana informacji — wymiana dokony-
wana w sposób dokładny i terminowy.
Komunikowanie się uczestników może przyjmować różne formy, zależnie od tego,
jakiego rodzaju aktywności jest podporządkowane. Uczestnicy raportują status swej pracy
w trakcie regularnych spotkań i utrwalają tę informację w protokołach. Status projektu ko-
munikowany jest regularnie klientowi w ramach przeglądu. Wymagania i zmiany komuni-
kowane są przy wsparciu modeli i towarzyszącej im dokumentacji. Sytuacje kryzysowe i nie-
porozumienia rozstrzygane są w ramach spontanicznej komunikacji telefonicznej, e-mailowej
lub bezpośredniej. W miarę jak projekt staje się coraz obszerniejszy, jego uczestnicy poświęcać
muszą więcej czasu na wzajemne komunikowanie się, co może odbywać się kosztem zasad-
niczych, technicznych aktywności. By poradzić sobie z tymi wyzwaniami, konieczne jest od-
powiednie zorganizowanie uczestników w zespoły oraz zapewnienie efektywnego obiegu in-
formacji za pomocą kanałów formalnych i mniej formalnych.
Rozpoczniemy ten rozdział od opisania podstawowych koncepcji związanych z organi-
zacją projektu — opiszemy zadania, produkty w ogólności i produkty docelowe. Następnie
omówimy mechanizmy komunikacyjne, jakie mają do dyspozycji uczestnicy projektu. Na
koniec przejdziemy do aktywności związanych z organizacją projektu i komunikowaniem się
jego uczestników. W tym rozdziale prezentujemy punkt widzenia uczestnika projektu (na
przykład programisty), który powinien jedynie rozumieć infrastrukturę organizacyjną i ko-
munikacyjną projektu; budowanie tej struktury jest zadaniem menedżerów i będzie przed-
miotem rozważań rozdziału 14. „Zarządzanie projektem".
114 Rozdziat 3. • Organizacja projektu i komunikacja

3.1. Wstęp — katastrofa Ariane


Realizując system informatyczny, programiści koncentrują się na zgodności jego zachowania
ze specyfikacją. Gdy współdziałają z innymi uczestnikami projektu, muszą komunikować się
z nimi w sposób precyzyjny i efektywny. I jeśli nawet komunikowanie to nie wydaje się ak-
tywnością kreatywną ani wyzywającą, jest dla powodzenia przedsięwzięcia nie mniej ważne
niż dobry projekt czy efektywna implementacja — o czym świadczyć może choćby poniższy
przykład [Lions, 1996].

Ariane 501
4 czerwca 1996 roku rakieta Ariane 501 — pierwszy prototyp serii Ariane 5 — eksplodowała pół
minuty po starcie. Oprogramowanie głównego komputera nawigacyjnego zakończyło swą pracę awa-
ryjnie z powodu wystąpienia nadmiaru operacji arytmetycznej; główny komputer oddelegował
kontrolę do komputera zapasowego i wyłączył się. Niestety, komputer zapasowy wyłączył się uła-
mek sekundy wcześniej z identycznej przyczyny. W efekcie rakieta, gwałtownie pozbawiona
systemu nawigacyjnego, dokonała ostrego skrętu, mającego na celu skorygowanie zboczenia z kursu,
które w rzeczywistości nie wystąpiło.

Wyjaśnianie łańcucha przyczynowo-skutkowego, prowadzącego od błędu w oprogramowaniu


do fatalnego końca rakiety trwało niecałe dwa miesiące. System nawigacyjny serii Ariane 5 bu-
dowany był z wielu komponentów serii Ariane 4, które w tejże serii spisywały się bez zarzutu.

System nawigacyjny rakiety odpowiedzialny jest za obliczanie korekcji odchyleń kursu od założonej
trajektorii na podstawie informacji pochodzącej z systemu referencyjnego, mającego naturę bezwład-
nościową, złożonego głównie z żyroskopów i mierników przyspieszenia. System ten stanowi jedyny
punkt odniesienia dla obliczania aktualnej pozycji rakiety — obliczanie to odbywa się więc w całko-
witym oderwaniu od świata zewnętrznego. Niezbędne jest wobec tego odpowiednie zainicjowanie
owego systemu referencyjnego, przez skorelowanie jego stanu z tymże światem zewnętrznym oraz
z aktualną pozycją rakiety. Inicjowanie to odbywa się bezpośrednio przed startem i trwa w przybliżeniu
45 minut; po starcie rakiety dane z systemu przekazywane są do komputerowego systemu nawigacyj-
nego, który po uwzględnieniu dodatkowych czynników (głównie ruchu obrotowego Ziemi) oblicza
bieżącą pozycję rakiety i konfrontuje ją z pozycją oczekiwaną w danej chwili; obliczenia te trwają ok.
50 sekund i w takim też odstępie odbywa się wspomniane kontrolowanie położenia rakiety. Odliczanie
do startu może być wstrzymane na żądanie; jego wznowienie nie wymaga powtórzenia owych
45-minutowych obliczeń inicjacyjnych. Po udanym starcie komputer pokładowy jeszcze przez
40 sekund generuje dane inicjacyjne, które jednak są wtedy całkowicie bezużyteczne.

System komputerowy serii Ariane 5 różnił się od tego z serii Ariane 4 między innymi zdublowaniem
platformy sprzętowej — dublowany był sam system referencyjny, komputery wykonujące niezbędne
obliczenia oraz urządzenia korygujące położenie rakiety; w przypadki awarii któregoś z podzespołów
jego funkcje automatycznie przejmowała replika.

System inicjacyjny, zaprojektowany wyłącznie do obliczeń naziemnych, wykorzystywał 16-bitowe


słowo do reprezentowania poziomej prędkości rakiety — co wydawało się w zupełności wystarczające
nawet przy uwzględnieniu czynników zaburzających, głównie wiatru i ruchu wirowego Ziemi.
W 30 sekund po starcie rakiety błąd nadmiaru arytmetycznego unieruchomił jednak oba systemy
komputerowe, choć ich sprzęt sprawował się nienagannie.
3.2. O projekcie ogólnie 115

Przyczyna. Oprogramowanie komputerów nie zostało wystarczająco przetestowane: mimo iż do-


świadczyło tysięcy różnych testów, żaden z nich nie był przeprowadzony w warunkach symulujących
lot po rzeczywistej orbicie. Równie wyczerpująco przetestowane zostało działanie systemu referen-
cyjnego, jego twórcy nie uwzględnili jednak możliwości kompletnego załamania się pokładowego
systemu komputerowego — ten przecież był zdublowany, więc w oczekiwaniu wysoce niezawodny.
Pierwotną przyczyną całej katastrofy okazał się zatem brak komunikacji między programistami
a zespołem odpowiedzialnym za system referencyjny.

Mimo iż w tym rozdziale dyskutujemy kwestie organizacyjne i komunikacyjne w odnie-


sieniu do projektów programistycznych, prezentowane koncepcje nie są specyficzne dla inży-
nierii oprogramowania; jednakże w przypadku projektu programistycznego sprawna i ade-
kwatna komunikacja nabiera szczególnego znaczenia, bowiem jej niedociągnięcia skutkować
mogą poważnymi, często nawet fatalnymi konsekwencjami dla projektu oraz jakości two-
rzonego systemu.

3.2. O projekcie ogólnie


Przedstawiona w poprzednim rozdziale notacja UML pozwala uczestnikom projektu na two-
rzenie odpowiednich modeli systemu jako podstawowego środka komunikowania skompli-
kowanych koncepcji. Modele te nie są jednak jedynym czynnikiem komunikacyjnym, bowiem
dla uczestników projektu interesujących jest jeszcze wiele innych kwestii. Oto niektóre z nich.

• Kto jest odpowiedzialny za poszczególne części systemu?


o Jakie są terminy dostarczenia poszczególnych części systemu?
• Z kim należy się kontaktować w przypadku problemów z konkretną wersją kon-
kretnego komponentu?
• Jak należy dokumentować napotykane problemy?
• Jakie są kryteria jakościowej oceny systemu?
• W jakiej formie nowe wymagania powinny być komunikowane programistom?
• Kto powinien być informowany o nowych wymaganiach?
• Kto jest odpowiedzialny za kontakty z klientem?

Mimo iż powyższe kwestie bywają łatwe do rozstrzygnięcia przy popołudniowej kawie,


w przypadku realizacji złożonego projektu takie techniki komunikacji ad hoc okazują się
znacznie mniej skuteczne. Z punktu widzenia programisty, projekt informatyczny postrze-
gany jest w kategoriach czterech głównych komponentów, którymi są (patrz rysunek 3.1):

• Produkt — pod tym pojęciem rozumiemy jakikolwiek wytwór działań związanych


z projektem: fragment kodu źródłowego, model, dokument i tym podobne. Produkt
wytworzony dla klienta określa się mianem produktu docelowego.
• Harmonogram — określa szczegółowo terminy wykonania poszczególnych prac
związanych z projektem.
116 Rozdziat 3. • Organizacja projektu i komunikacja

Rysunek 3.1. Model projektu z perspektywy programisty

• Uczestnik — to każda osoba zaangażowana w realizację projektu; uczestnicy na-


zywani są także członkami projektu.
• Zadanie — realizuje je uczestnik projektu w celu wytworzenia produktu.

Projekt może być zdefiniowany w sposób formalny lub nieformalny. Pisemny kontrakt
między wykonawcą a klientem, uzgadniający dostarczenie systemu informatycznego w termi-
nie trzech miesięcy za cenę miliona dolarów, to nic innego jak formalne określenie projektu;
projektem może być jednak również nieformalna obietnica zainstalowania nowego opro-
gramowania na komputerze kolegi, w przyszłym tygodniu.
Projekty bywają zróżnicowane pod względem typu i rozmiaru. Niekiedy typ projektu
definiuje się na podstawie natury jego produktu docelowego — i tak na przykład stworzenie
systemu informatycznego dla celów zarządzania księgowością przedsiębiorstwa określa się
mianem projektu programistycznego, natomiast budowa promu kosmicznego zaliczana jest
do projektów systemowych. Projekty mogą mieć rozmiary banalne albo gigantyczne: budowa
promu kosmicznego, wraz z niezbędną infrastrukturą informatyczną, trwająca 10 - 15 lat
i wyceniona na 10 miliardów dolarów, zdecydowanie zalicza się do tych drugich, w odróżnie-
niu od np. przemeblowywania mieszkania, reprezentującego zdecydowanie pierwszą kategorię.
Gdy spojrzeć na realizację projektu pod kątem dynamicznym, wyróżnić można kilka
jej faz, przedstawionych symbolicznie na rysunku 3.2.

Rysunek 3.2. Stany realizacji projektu

W fazę definiowania projektu zaangażowani są zwykle: menedżer, przedstawiciel klienta,


główny architekt oprogramowania i główny analityk. W fazie tej następuje skupienie się na
dwóch głównych problemach: wstępnym rozpoznaniu architektury oprogramowania, ze szcze-
3.2. O projekcie ogólnie 117

gólnym uwzględnieniem dekompozycji' systemu na podsystemy, oraz wstępnym sformuło-


waniu projektu w kategoriach zadań do wykonania, harmonogramu i niezbędnych zasobów.
Znajduje to odzwierciedlenie w trzech dokumentach: deklaracji problemu, zarysie architektury
oprogramowania i wstępnym planie zarządzania projektem. W fazie startowej menedżer
projektu buduje jego infrastrukturę, zatrudnia uczestników, organizuje ich w zespoły, definiuje
najważniejsze „kamienie milowe" i doprowadza do rozpoczęcia realizacji projektu. W tej
i w poprzedniej fazie większość decyzji podejmowana jest przez menedżera. W fazie ustalonej
do działania przystępują programiści i projektanci. Ich prace nadzorowane są przez kie-
rowników zespołów — kierownicy ci odpowiedzialni są za śledzenie postępu prac w swoich
zespołach i identyfikowanie problemów, jakie się w związku z tymi pracami pojawiają. Są
oni jednocześnie odpowiedzialni za raportowanie sytuacji menedżerowi projektu, który na
tej podstawie ocenia status realizacji projektu jako całości, odpowiadają też za zmianę przy-
działu zadań i realokację zasobów w związku ze zmianami w harmonogramie. Menedżer pro-
jektu jest natomiast odpowiedzialny za współdziałanie z klientem i formalizację porozumień,
jak również za negocjacje w kwestii niezbędnych zasobów i harmonogramu. W ostatniej fazie
— fazie terminalnej — kompletny system dostarczany jest klientowi, jednocześnie kompleto-
wana jest historia projektu. W fazie tej kończy się zaangażowanie większości programistów
w projekt, niektórzy z nich, m.in. kierownicy zespołów, przy udziale dokumentalistów, zajmują
się instalowaniem systemu i zebraniem historii projektu wykorzystywanej w przyszłości.
Komunikacja w ramach projektu związania jest ze zdarzeniami dwóch kategorii:
przewidywalnymi i nieprzewidywalnymi. Do kategorii pierwszej zaliczyć można między
innymi:

• inspekcję problemu, podczas której programiści uzyskują niezbędne dla siebie in-
formacje z zakresu dziedziny aplikacyjnej — na podstawie deklaracji problemu oraz
interakcji z klientem i przyszłymi użytkownikami systemu;
• zebrania statusowe, w ramach których zespoły analizują postęp swoich prac;
• przeglądy partnerskie, obejmujące identyfikowanie usterek we wstępnych wersjach
produktów oraz poszukiwanie sposobów niwelowania tych usterek;
• przeglądy kliencki i projektowy, w czasie których klient i inni uczestnicy dokonują
oceny jakości produktów, szczególnie produktów docelowych;
o emisje, polegające na przekazaniu klientowi i użytkownikom kolejnych wersji pro-
duktów docelowych wraz z niezbędną dokumentacją.

Do zdarzeń nieprzewidywalnych należą natomiast przede wszystkim:

• żądanie wyjaśnień, czyli żądanie przez uczestnika dodatkowej informacji dotyczącej


systemu, dziedziny aplikacyjnej lub projektu;
• żądanie zmian, wskutek problemów napotykanych w związku z tworzonym syste-
mem bądź propozycją nowych jego cech;
• rozwiązywanie problemów, które mogą mieć postać konfliktów między uczestni-
kami bądź charakter czysto techniczny (bądź plasować się na pograniczu jednego
i drugiego).
118 Rozdziat 3. • Organizacja projektu i komunikacja

Komunikacja związana ze zdarzeniami przewidywalnymi, zwana krótko komunikacją


planową, pomaga w rozpowszechnianiu informacji, którą zainteresowani uczestnicy spo-
dziewają się uzyskiwać. Komunikacja pozaplanowa, czyli związana ze zdarzeniami nieprze-
widywalnymi, pomaga w rozwiązywaniu sytuacji kryzysowych. Niezależnie od rodzaju, za-
równo potrzeby w zakresie komunikacji, jak i sama komunikacja, powinny mieć charakter
precyzyjny i efektywny.
Gdy programiści dołączają do projektu w jego fazie startowej, istnieje już deklaracja
problemu, menedżer projektu sporządził wstępny plan rozwiązywania głównych projektów,
ustanowił organizację projektu, zdefiniował przewidywalne zdarzenia komunikacyjne i do-
starczył infrastrukturę niezbędną do komunikacji zarówno planowej, jak i pozaplanowej.
Większość wysiłku programistów koncentruje się wówczas na zrozumieniu związanych z nią
dokumentów i przystosowaniu się do istniejących struktur organizacyjnych oraz komunika-
cyjnych. Wiążą się z tym następujące aktywności:

» Udział w zebraniach początkujących. W ich ramach programiści zapoznają się z po-


trzebami klienta oraz zakresem systemu, nad którym będą pracować. Prowadzi to
do wysokopoziomowego zrozumienia systemu, co stanowi podstawę dla wszystkich
innych aktywności.
o Dołączenie do zespołu. Menedżer projektu, dokonując podziału projektu między
poszczególne zespoły, ustala jednocześnie skład osobowy poszczególnych zespołów;
kieruje się przy tym kwalifikacjami i zainteresowaniami poszczególnych programi-
stów. Może też zdecydować o dodatkowym przeszkoleniu programistów niepo-
siadających wystarczających kwalifikacji w pewnym zakresie.
® Udział w sesjach treningowych. Ich celem jest zdobycie przez programistów dodat-
kowych umiejętności, niezbędnych dla niektórych zadań.
• Dołączenie do infrastruktury komunikacyjnej. Na tę infrastrukturę składają się roz-
maite mechanizmy w rodzaju narzędzi do komunikacji grupowej, książek adre-
sowych, książek telefonicznych, usług e-mail i wyposażenia do telekonferencji.
• Rozbudowywanie infrastruktury komunikacyjnej. W miarę potrzeb początkowa
infrastruktura może być poszerzana o grupy dyskusyjne czy portale tematyczne zwią-
zane z projektem.
• Udział w pierwszym zebraniu statusowym. W ramach tego zebrania programiści
zapoznawani są z procedurami zebrań statusowych oraz procedurami rejestrowania
informacji o statusie prac i dostarczania tych informacji innym uczestnikom projektu.
• Zrozumienie harmonogramu przeglądów. Zadaniem harmonogramu przeglądów
jest określenie terminów „kamieni milowych", w związku z którymi rezultaty reali-
zacji projektu komunikowane będą menedżerowi i klientowi w formie przeglądu.
Celem takich przeglądów jest zarówno informowanie członków innych zespołów
o statusie prac nad wybranym obszarem projektu i o ewentualnych problemach
dotyczących tego obszaru, jak i prezentowanie klientowi bieżącego statusu pro-
jektu oraz uwzględnianie jego uwag w tej kwestii.

W kolejnych sekcjach omówimy szczegółowo przedstawione koncepcje i aktywności.


W sekcji 3.3 opiszemy zespołową organizację projektu, w sekcji 3.4 dyskutować będziemy
3.3. Koncepcje organizacyjne projektu 119

koncepcje powiązane z komunikacją w ramach projektu; w sekcji 3.5 przedstawimy szcze-


gółowo „startowe" aktywności typowe dla programisty pracującego w zespole. Czytelnikom
zainteresowanym dalszymi informacjami dotyczącymi opisywanych zagadnień proponujemy
w sekcji 3.6 wybraną literaturę uzupełniającą.
W rozdziale tym skupimy się na spojrzeniu na projekt z perspektywy programisty do-
łączającego do zespołu, nie będziemy zatem poruszać kwestii związanych z budowaniem or-
ganizacji projektu i jego infrastruktury komunikacyjnej ani też z zarządzaniem tymi pod-
miotami: dyskusję tę odkładamy bowiem do rozdziałów 12. „Zarządzanie racjonalizacją",
13. „Zarządzanie konfiguracją" i 14. „Zarządzanie projektem", poświęconych identyfiko-
waniu, negocjowaniu, rozwiązywaniu i dokumentowaniu problemów, zarządzaniu wersjami,
konfiguracją i emisjami oraz spojrzeniu na organizację i komunikację z perspektywy me-
nedżera projektu.

3.3. Koncepcje organizacyjne projektu


W tej sekcji przedstawimy definicje takich pojęć jak:

• Organizacja projektu (patrz sekcja 3.3.1),


• Rola (patrz sekcja 3.3.2),
• Zadanie i produkt (patrz sekcja 3.3.3),
® Harmonogram (patrz sekcja 3.3.4).

3.3.1. Organizacja projektów


Istotną częścią organizacji projektu jest zdefiniowanie relacji między poszczególnymi uczest-
nikami, a także ich relacji do poszczególnych zadań, harmonogramu i produktów. W ramach
organizacji zespołowej (patrz rysunek 3.3) uczestnicy pogrupowani są w małe zespoły, z któ-
rych każdy realizuje określone zadanie lub aktywność. Będziemy odróżniać zespoły od innych
rodzajów zbiorowisk ludzkich, szczególnie od grup i komisji. Grupa ludzi przypisana jest,
co prawda, do realizacji wspólnego zadania, lecz każdy jej członek działa indywidualnie, bez
potrzeby komunikowania się z pozostałymi członkami. Zadaniem komisji jest natomiast
przegląd i krytyczna ocena rozwiązań oraz formułowanie nowych propozycji.

Rysunek 3.3. Zespołowa organizacja projektu: jednostkę organizacji stanowi zespół, składający się z pro-
gramistów lub innych zespołów

Na rysunku 3.4 przedstawiono diagram odzwierciedlający organizację prostego pro-


jektu, w ramach którego zespołowi zarządzającemu podporządkowane są trzy zespoły pro-
gramistów.
120 Rozdziat 3. • Organizacja projektu i komunikacja

Rysunek 3.4. Przykład prostej organizacji projektu: raportowanie, decydowanie i komunikowanie reali-
zowane są przez skojarzenie agregacyjne z poziomem organizacyjnym

Wśród różnych interakcji występujących między uczestnikami projektu do trzech naj-


ważniejszych należą:

• raportowanie — ma na celu przedstawianie informacji o bieżącym statusie projektu;


przykładowo programista odpowiedzialny za API modułu raportuje innym progra-
mistom szczegóły tego API, natomiast kierownik projektu może informować mene-
dżera projektu o tym, że przypisane zespołowi zadanie nie zostało jeszcze wykonane.
• decydowanie — oznacza ogłaszanie podejmowanych decyzji; przykładowo kierownik
zespołu decyduje o opublikowaniu API modułu przez programistę odpowiedzialnego
za ten moduł, menedżer projektu decyduje o zmianach w harmonogramie itp.; celem
decyzji może być także arbitralne rozstrzygnięcie konfliktu lub wątpliwości.
• komunikowanie — oznacza wymianę wszelkich innych informacji związanych ze
statusem lub podejmowanymi decyzjami; komunikacja może przyjmować różne
formy, na przykład wymiany wymagań czy modeli, formułowania propozycji roz-
wiązań czy dostarczania argumentów do dyskusji; zaproszenie na obiad też jest prze-
jawem komunikowania się.

Organizację projektu nazywamy hierarchiczną, jeśli zarówno informacja o statusie, jak


i procesy decyzyjne mają charakter jednokierunkowy — decyzje podejmowane są w korzeniu
drzewa organizacyjnego i przesyłane w głąb niego, w kierunku liści, natomiast informacja
o statusie generowana jest w węzłach wewnętrznych lub liściach i przesyłana w kierunku
korzenia. Charakter przebiegu informacji o statusie i informacji decyzyjnych nazywany bywa
często strukturą raportowania organizacji. Na rysunku 3.5 widzimy przykład hierarchicznej
organizacji zespołowej.
W organizacjach typowo hierarchicznych, na przykład w wojsku,, struktura raportowania
jest jedyną oficjalną drogą przepływu informacji. W złożonych projektach programistycznych
taka organizacja ma swoje wady i może stwarzać problemy: przykładowo wiele decyzji o cha-
rakterze technicznym powinno być podejmowane lokalnie, w ramach zespołu, lecz w zależno-
ści od informacji pochodzącej od programistów z innych zespołów. Gdyby informację tę uzy-
skiwać wyłącznie w ramach organizacyjnej struktury raportowania, proces decyzyjny mógłby
zostać znacząco spowolniony. Co gorsza, zaistniałoby duże ryzyko zniekształcenia informacji,
ze względu na jej złożoność i rozmiar.
3.3. Koncepcje organizacyjne projektu 121

Rysunek 3.5. Przykład hierarchicznej struktury raportowania: informacja o statusie projektu raportowana
jest menedżerowi projektu, który następnie przekazuje swe decyzje programistom za pośrednictwem
kierowników zespołów; menedżer wraz w kierownikami zespołów tworzą zespół zarządzający

Konieczne jest więc ustanowienie dodatkowej struktury umożliwiającej bezpośrednią


wymianę informacji między uczestnikami projektu, z pominięciem hierarchicznej struktury
raportowania. Jedną z nich jest struktura oparta na łącznikach: funkcję tę pełni wybrany pro-
gramista, którego zadaniem jest przekazywanie informacji w obie strony. Na rysunku 3.6 wi-
dzimy przykład organizacji, w ramach której wymiana informacji w oparciu o łączników uzu-
pełnia oficjalną strukturę raportowania. Przykładowo zespół dokumentacyjny posiada w swym
składzie łącznika kontaktującego się z zespołem interfejsu użytkownika, którego zadaniem jest
informowanie na bieżąco o zmianach, jakie dokonywane są w kwestii dialogu użytkownika
z systemem. Zespoły, które nie zajmują się bezpośrednio konkretnymi podsystemami, lecz
wykonują zadania krzyżujące się z podziałem opartym na podsystemach, nazywane są zespo-
łami międzyfunkcyjnymi. Zespoły takie zajmują się m.in. opracowywaniem architektury sys-
temu, testowaniem i dokumentowaniem.

Rysunek 3.6. Przykład struktury łącznikowej. Zespół składa się z pięciorga programistów: Alice jest
kierownikiem, a więc również łącznikiem z zespołem zarządzającym; John jako inżynier API jest łączni-
kiem z zespołem architektonicznym; Mary jest łącznikiem z zespołem dokumentacyjnym; Chris i Sam,
jako implementatorzy, kontaktują się z innymi zespołami jedynie w sposób nieformalny
122 Rozdziat 3. • Organizacja projektu i komunikacja

Strukturę komunikacyjną i organizację bazującą na łącznikach nazywamy (odpowied-


nio) strukturą łącznikową i organizacją łącznikową. W strukturze tej odpowiedzialność kie-
rowników zespołów rozszerzona zostaje o nową funkcję: oprócz reprezentowania zespołu
wobec menedżera projektu, kierownik zespołu pośredniczy także w przekazywaniu informacji
między programistami z różnych zespołów. By mógł pełnić tę funkcję efektywnie, konieczne
jest zapewnienie sprawnych ścieżek komunikacyjnych.
Programiści z różnych zespołów mogą także komunikować się bezpośrednio, z pomi-
nięciem swoich kierowników. Ten rodzaj komunikacji nazywamy komunikacją partnerską.

3.3.2. Role w realizacji projektu


Pod pojęciem roli rozumiemy zbiór zadań o charakterze technicznym łub menedżerskim,
przydzielonych uczestnikowi lub zespołowi. Przykładowo rola testera w zespole zajmującym
się konkretnym podsystemem obejmuje opracowywanie zestawów testowych dla powstającego
podsystemu, wykonywanie testowania w oparciu o te zestawy oraz raportowanie innym pro-
gramistom wykrytych różnic i usterek. W typowym projekcie programistycznym rola, w jakiej
obsadzony zostaje zespół lub uczestnik, może być rolą jednego z czterech typów: zarządczą,
programistyczną, międzyfunkcyjną lub konsultacyjną (patrz rysunek 3.7).

Rysunek 3.7. Typy ról spotykanych w realizacji projektów programistycznych

W roli o charakterze zarządczym obsadzani są między innymi menedżer projektu i kie-


rownicy zespołów. Ten typ ról związany jest z organizacją projektu oraz jego realizacją w ramach
istniejących warunków i uzgodnionych ograniczeń. Rolami tego typu zajmiemy się dokładniej
w rozdziale 14. „Zarządzanie projektem".
3.3. Koncepcje organizacyjne projektu 123

Role o charakterze programistycznym to role między innymi analityka, architekta sys-


temu, projektanta obiektów, implementatora i testera, czyli role obejmujące zadania ukie-
runkowane na specyfikowanie, projektowanie, konstruowanie i testowanie podsystemów. Ich
omówieniem zajmiemy się w rozdziałach od 5. do 11.; w tabeli 3.1 wymieniamy natomiast
kilka przykładów ról tego typu.

Tabela 3.1. Przykłady ról programistycznych

Rola Zakres odpowiedzialności


Architekt systemu Zadaniem architekta systemu jest zapewnienie spójności rozwiązań
z założeniami projektowymi i stylem interfejsów. Spójność z założeniami
projektu realizowana jest poprzez sformułowanie wymogów dotyczących
zarządzania konfiguracją oraz kryteriów integrowania podsystemów.
Rola architekta jest więc rolą typowo integracyjną, opierającą się na
informacjach pochodzących od zespołów zajmujących się poszczególnymi
podsystemami.

Projektant obiektów Projektant obiektów odpowiedzialny jest za zdefiniowanie interfejsu


podsystemu. Interfejs ten powinien odzwierciedlać zarówno funkcjonalność
założoną w projekcie podsystemu, jak również potrzeby komunikujących
się z nim innych podsystemów. Gdy w podsystemie dokonywane są zmiany
funkcjonalne, podyktowane potrzebami innych podsystemów, projektant
obiektów odpowiedzialny jest za przekazanie związanych z tym informacji
programistom z zespołu zajmującego się tym podsystemem.

Implementator Rola implementatora sprowadza się do stworzenia kodu źródłowego klas


związanych z danym podsystemem.

Tester Zadaniem testera jest weryfikowanie zgodności rzeczywistego zachowania


podsystemu z założeniami zdefiniowanymi przez projektanta obiektów.
Często w projekcie informatycznym tworzy się odrębny zespół zajmujący
się jedynie testowaniem. Rozdzielenie ról implementatora i testera skutkuje
zwykle bardziej owocnym testowaniem.

Istotą ról iniędzyfunkcyjnych jest koordynowanie działań poszczególnych zespołów.


Programiści pełniący role tego rodzaju odpowiedzialni są za wymianę informacji z innymi
zespołami i negocjowanie z nimi szczegółów interfejsów. Programista obsadzony w roli mię-
dzyfunkcyjnej nazywany jest powszechnie łącznikiem, jako że odpowiada za przekazywanie
informacji między zespołami. W niektórych przypadkach łącznik (będący na przykład inży-
nierem API) reprezentuje zespół w kontaktach z innymi zespołami. Wyróżniamy cztery pod-
stawowe typy łączników:

• Inżynier API odpowiedzialny jest za interfejs API konkretnego podsystemu. In-


terfejs ten reprezentuje funkcjonalność podsystemu wobec innych podsystemów,
powinien zatem odzwierciedlać ich potrzeby. Wynikające stąd zmiany powinny
być przekazywane programistom podsystemu, za co również odpowiedzialny jest
inżynier API.
• Redaktor dokumentacji odpowiada za integrowanie dokumentów sporządzanych
przez zespól. Z perspektywy innych zespołów, zależnych od podsystemu tworzonego
124 Rozdziat 3. • Organizacja projektu i komunikacja

przez jego macierzysty zespół, redaktor postrzegany jest jako dostawca usługi. Zaj-
muje się także dokumentowaniem informacji wewnątrzzespołowej, powstającej
między innymi w ramach spotkań i narad zespołu.
• Menedżer konfiguracji to rola związana z zarządzaniem różnymi wersjami doku-
mentów, modeli i fragmentów kodu, produkowanych w ramach zespołu. Gdy zasady
owego zarządzania nie są skomplikowane, rolę menedżera konfiguracji może pełnić
kierownik zespołu.
• Tester odpowiada za zgodność rzeczywistego funkcjonowania podsystemu ze spe-
cyfikacją jego projektanta. Często testowanie powierza się odrębnemu zespołowi,
który nie zajmuje się programowaniem — takie rozdzielenie ról implementatora
i testera skutkuje zwykle bardziej efektywnym testowaniem.

Konsultanci zapewniają tymczasowe wsparcie w obszarach, względem których uczest-


nicy projektu posiadają niewystarczającą wiedzę, kwalifikacje lub doświadczenie. W większości
przypadków klient i użytkownicy pełnią rolę konsultantów z zakresu dziedziny aplikacyjnej.
Konsultant techniczny może dostarczać zaawansowaną wiedzę na temat nowych metod i tech-
nologii, zaś konsultanci nietechniczni mogą być pomocni w rozwiązywaniu problemów natury
prawnej lub marketingowej. Wśród ról tego typu najczęściej spotyka się następujące:

• Klient — jest odpowiedzialny za sformułowanie wymagań i scenariuszy. Pod poję-


ciem „wymagań" rozumiemy tu wymagania funkcyjne i pozafunkcyjne oraz ograni-
czenia. Powodzenie projektu wymaga zazwyczaj intensywnych interakcji między
klientem a programistami.
• Użytkownik — to osoba, która korzystać będzie z tworzonego systemu. Niekiedy
realizacja projektu odbywa się w oderwaniu od użytkowników, często też potencjalni
użytkownicy tworzonego systemu nie znani są a priori — wówczas użytkownik re-
prezentowany jest przez klienta, czy nawet przez wyznaczonego programistę.
• Specjalista z zakresu dziedziny aplikacyjnej — zwany także „specjalistą od za-
stosowań" odpowiedzialny jest za dostarczenie wiedzy dotyczącej szczegółów wy-
branej kategorii funkcjonalności systemu. Podczas gdy klient dysponuje ogólną
wiedzą na temat wymaganej funkcjonalności, specjalista od zastosowań dostarcza
wiedzę związaną z konkretnym problemem.
• Specjalista z zakresu dziedziny realizacyjnej — to dostawca wiedzy na temat do-
stępnych rozwiązań w zakresie implementacji systemu. Wiedza ta obejmować może
szczegóły algorytmów, metod programowania, procesów, technologii implementa-
cyjnych lub środowisk i narzędzi programistycznych.

3.3.3. Zadania i produkty


Zadaniem nazywamy dobrze zdefiniowany przydział pracy, wynikający z przydzielonej roli.
Grupy powiązanych ze sobą zadań nazywane są aktywnościami. Menedżer projektu lub kie-
rownik zespołu obsadza uczestnika w nowej roli, po czym kontroluje stan wykonywania wy-
nikających z tej roli zadań. Produkt jest uchwytnym efektem wykonania zadania — modelem,
diagramem klas, fragmentem kodu źródłowego, dokumentem lub jego częścią albo czymś
3.3. Koncepcje organizacyjne projektu 125

p o d o b n y m . P r o d u k t y s t a n o w i ą rezultaty w y k o n y w a n i a o k r e ś l o n y c h z a d a ń , są p o d s t a w ą f o r -
m u ł o w a n i a h a r m o n o g r a m ó w i w a r u n k u j ą w y k o n y w a n i e i n n y c h z a d a ń . P r z y k ł a d o w o aktyw-
n o ś ć p l a n o w a n i a t e s t ó w dla p o d s y s t e m u z a r z ą d z a n i a b a z ą d a n y c h s k u t k u j e p r o d u k t e m w p o -
staci zestawu p r z y p a d k ó w testowych i ich o c z e k i w a n y c h rezultatów, k t ó r e s t a n o w i ą m a t e r i a ł
n i e z b ę d n y dla i n n e j a k t y w n o ś c i — t e s t o w a n i a d a n e g o p o d s y s t e m u ( p a t r z r y s u n e k 3.8).

Rysunek 3.8. Produkty podsystemu zarządzania bazą danych. Skojarzenia reprezentują zależności między
produktami

P r o d u k t p r z e z n a c z o n y dla klienta nazywa się p r o d u k t e m d o c e l o w y m . Działający s y s t e m


w r a z z n i e z b ę d n ą d o k u m e n t a c j ą s k ł a d a się zwykle z c a ł e g o z b i o r u p r o d u k t ó w d o c e l o w y c h .
P r o d u k t n i e w i d o c z n y dla klienta, p r z e z n a c z o n y n a w e w n ę t r z n y u ż y t e k s y s t e m u , n a z y w a m y
p r o d u k t e m w e w n ę t r z n y m . P r z y k ł a d y p r o d u k t ó w w e w n ę t r z n y c h p r e z e n t u j e m y w tabeli 3.2.

Tabela 3.2. Opis produktów wewnętrznych przedstawionych na rysunku 3.8

Produkt Typ Opis

Obiekty trwałe Model klas Ten model klas opisuje wyczerpująco obiekty przeznaczone
do trwałego przechowywania w bazie danych stanowiącej
przedmiot podsystemu. Dla każdej klasy definiowane są
atrybuty, skojarzenia, krotności i role.

Obiekty projektu Model klas Ten model opisuje inne obiekty niezbędne do przechowywania
danych w bazie, niebędące obiektami trwałymi.

Plan testów Dokument Dokument ten opisuje strategie testowanie, kryteria testowania
i przypadki testowe dedykowane wykrywaniu usterek tkwiących
w implementacji podsystemu zarządzania bazą danych.

Usterki wykryte Dokument Dokument ten opisuje usterki wykryte podczas inspekcji
podczas inspekcji kodu kodu dokonywanej przez programistów zespołu. Każdej
wykrytej usterce towarzyszy propozycja jej naprawienia.

Usterki wykryte Dokument W tym dokumencie opisane są usterki podsystemu


podczas testowania zarządzania bazą danych, zauważone w trakcie testowania.
126

Specyfikacja prac prowadzących do wykonania zadania lub aktywności ma formę pakietu.


Elementami pakietu są: jego nazwa, opis przedmiotowego zadania, zasoby konieczne do jego
wykonania, produkty wejściowe (wytworzone przez inne zadania) i wyjściowe (wyproduko-
wane przez przedmiotowe zadanie) oraz zależności od innych zadań. Na rysunku 3.9 widoczny
jest schemat zależności między pakietami, aktywnościami, zadaniami, rolami i produktami.

Rysunek 3.9. Zależności między zadaniami, aktywnościami, rolami, produktami i pakietami

W tabeli 3.3 przedstawiamy przykłady pakietów.


Produkty są ważnymi podmiotami w zarządzaniu projektem, ponieważ stanowią pod-
stawę do szacowania terminów rozpoczynania i kończenia zadań oraz dostarczania innych
produktów. Przykładowo opóźnienie dostarczenia zestawów testowych skutkuje opóźnieniem
rozpoczęcia procesu testowania. Zauważmy jednak, że skupienie się jedynie na czasowych
aspektach dostarczania produktów jest niewystarczające: terminowe dostarczenie kiepskiej
jakości zestawów testowych może spowodować, że wiele krytycznych usterek podsystemu nie
zostanie wykrytych, co ostatecznie opóźni dostarczenie gotowego systemu.

3.3.4. Harmonogramy
Harmonogram jest odwzorowaniem zadań na upływający czas: dla każdego zadania określa
się moment jego rozpoczęcia i zakończenia. Stanowi to podstawę do planowanie terminów
dostarczania poszczególnych produktów docelowych. Dwiema często używanymi notacjami
dla harmonogramów są wykresy Gantta [Gantt, 1910] i grafy PERT. Wykres Gantta jest
zwartą prezentacją przebiegu realizacji projektu w czasie: równolegle do poziomej osi czasu
lysowane są poziome słupki oznaczające poszczególne zadania: początek i koniec słupka od-
zwierciedla rozpoczęcie i zakończenie konkretnego zadania. Harmonogram w postaci wy-
kresu Gantta dla podsystemu zarządzania bazą danych pokazany jest na rysunku 3.10.
Graf PERT jest acyklicznym grafem skierowanym, którego wierzchołki reprezentują
zadania, a krawędzie — następstwo powiązanych zadań. Planowane rozpoczęcie i zakoń-
czenie każdego zadania staje się podstawą do wyznaczenia tzw. ścieżki krytycznej, będącej
najkrótszą ścieżką w grafie. Jej długość stanowi dolne ograniczenie na czas realizacji grupy
3.3. Koncepcje organizacyjne projektu 127

Tabela 3.3. Przykładowe pakiety związane z realizacją podsystemu zarządzania bazą danych

Zadanie W y n i k a z roli Opis Wejście Wyjście

Zbieranie Architekt systemu Zbieranie od Łącznicy API podsystemu,


wvmaqan poszczególnych diagram klas
dotvczacvch zespołów wymagań UML
podsystemu
dotyczących ich przedstawiający
zarządzani a
zapotrzebowania model analityczny
bazą danych
p o d względem obiektów trwałych
przechowywanych
obiektów trwałych
— ich atrybutów
i powiązań między nimi

Pro.iektowanie Projektant Projektowanie API Projekt


podsystemu obiektów podsystemu podsystemu podsystemu
zarzadzani a z uwzględnieniem w postaci
baza danych
wyboru wśród diagramu UML
rozwiązań
komercyjnych

Impl ementac.ia Implementator Implementacja Projekt Kod źródłowy


podsystemu podsystemu podsystemu podsystemu
zarządzani a
bazą danych
Inspekc.ia Implementator, Przeprowadzenie Kod źródłowy Lista usterek
kodu tester, projektant inspekcji kodu podsystemu
podsystemu obiektów źródłowego,
zarządzania
związanego
bazą danych
z implementacją
podsystemu
Planowanie Tester Opracowanie API Zestawy testowe
testów zestawów testowych podsystemu, i plan testowania
podsystemu dla podsystemu kod źródłowy
zarzadzania
podsystemu
bazą danych
Testowanie Tester Wykonanie testów Plan testowania Wyniki testów,
podsystemu podsystemu podsystemu, lista usterek
zarzadzania zestawy testowe
baza danych
dla podsystemu

zadań, pod w a r u n k i e m dostępności zasobów wystarczających dla pełnego wykorzystania


m o ż l i w o ś c i z r ó w n o ł e g l e n i a z a d a ń . Ścieżka k r y t y c z n a P E R T jest c z y n n i k i e m o tyle w a ż n y m
w h a r m o n o g r a m i e , że wydłużenie czasu realizacji jakiegokolwiek leżącego n a niej zadania nie-
u c h r o n n i e p r o w a d z i d o w y d ł u ż e n i a realizacji całego p r o j e k t u . Graf P E R T dla realizacji p o d -
s y s t e m u z a r z ą d z a n i a b a z ą d a n y c h p r z e d s t a w i o n y jest n a r y s u n k u 3.11; k r a w ę d z i e t w o r z ą c e
ścieżkę k r y t y c z n ą w y r ó ż n i o n e są p o g r u b i o n y m i l i n i a m i .
128 Rozdziat 3. • Organizacja projektu i komunikacja

Rysunek 3.10. Przykładowy harmonogram dla realizacji podsystemu zarządzania bazą danych — wykres
Gantta

Rysunek 3.11. Przykładowy harmonogram dla realizacji podsystemu zarządzania bazą danych — graf PERT.
Pogrubione krawędzie tworzą ścieżkę krytyczną

3.4. Koncepcje komunikacyjne projektu


Dotychczas rozważaliśmy głównie organizację projektu, obecnie przyjrzymy się bliżej samej
komunikacji jako elementowi tejże organizacji. Omówimy dwa główne typy komunikacji:
komunikację planową (sekcja 3.4.1) i komunikację pozaplanową (sekcja 3.4.2). Później zaj-
miemy się narzędziami usprawniającymi komunikację w ramach projektu (sekcja 3.4.3). Na
rysunku 3.12 przedstawiamy zależności występujące pomiędzy koncepcjami organizacyjnymi
a koncepcjami komunikacyjnymi.

3.4.1. Komunikacja planowa


Przewidywalnymi zdarzeniami komunikacyjnymi są zdarzenia określone przez poszcze-
gólne punkty harmonogramu. W ich ramach uczestnicy projektu wymieniają informacje na
określony temat, na przykład związane z przeglądem produktu. Zdarzenia te zostają sformali-
zowane i zorganizowane w odpowiednią strukturę, by zapewnić przepływ jak największej ilości
informacji w jednostce czasu, tak cennego dla uczestników. Do typowych przewidywalnych
zdarzeń wymagających komunikacji zaliczyć można między innymi:

• Prezentowanie problemu,
• Przegląd kliencki,
3.3. Koncepcje organizacyjne projektu 129

Rysunek 3.12. Relacje między koncepcjami komunikacyjnymi i organizacyjnymi

• Przegląd projektowy,
• Przegląd partnerski,
• Przegląd statusowy,
• Burzę mózgów,
• Emisje,
• Przegląd post mortem.
|
Przyjrzyjmy się pokrótce poszczególnym z nich.

Deklaracja problem u

Centralnym punktem prezentacji problemu jest dokument deklaracji problemu, zawie-


rający sformułowanie problemu, dziedziny aplikacyjnej i pożądanej funkcjonalności systemu.
Często deklaracja problemu zawiera także wymagania pozafunkcyjne, na przykład specyfikację
platformy sprzętowej lub programowej czy też oczekiwaną szybkość działania systemu. Poniżej
zamieszczamy fragment przykładowej deklaracji problemu.
130 Rozdziat 3. • Organizacja projektu i komunikacja

DEKLARACJA PROBLEMU OWL


1. Dziedzina aplikacyjna

Obowiązującym dziś t r e n d e m w przemyśle budowniczym jest dostarczanie rozproszonych usług


i kontrolowanie środowiska na poziomie poszczególnych użytkowników budynku, co w zamierzeniu
łagodzić ma konsekwencje nadmiernego uzależnienia od ogromnych scentralizowanych systemów,
charakterystycznych dla budynków biurowych wybudowanych w ostatnich trzydziestu latach.
W inteligentnym miejscu pracy pracownicy mają większą kontrolę nad elementami swego środo-
wiska — temperaturą, natężeniem oświetlenia, redukcją światła słonecznego, szybkością i kie-
runkiem nawiewanego powietrza i tak dalej (we wszystkie te udogodnienia wyposażony jest typowy
samochód — czemu więc nie ma być ich w biurze?). Energooszczędna fasada budynku powinna
zapewniać napływ świeżego powietrza oraz dostosowywanie natężenia światła wpadającego przez
okna, w celu jak najlepszego wykorzystywania światła dziennego na stanowisku pracy, przy jedno-
czesnej ochronie przed oślepiającym światłem słonecznym.

W inteligentnym miejscu pracy pożądane jest zaadaptowanie trzech form sterowania wyżej wy-
mienionymi czynnikami: reaktywnej, planowej i doraźnej. Kontrola reaktywna polega na samoczyn-
nym dostosowywaniu stanu wybranych komponentów do wskazań czujników; kontrola planowa
odnosi się do przewidywalnych zdarzeń, zjawisk i czynników, dzięki czemu stan wspomnianych
komponentów można bezpośrednio kontrolować i modyfikować, zgodnie ze starannie opracowa-
nym harmonogramem. Przykładem przewidywalnych danych jest pozycja Słońca, dzięki czemu re-
gulowanie dostępności światła dziennego może odbywać się w sposób a priori zaprogramowa-
ny. Sterowanie doraźne to nic innego jak możliwość bezpośredniego sterowania wymienionymi
czynnikami przez samych pracowników, stosownie do ich aktualnych potrzeb — jeśli będą chcieli
ingerować w automatyczną kontrolę swego środowiska pracy, należy im to umożliwić.

Przedmiotem tego projektu jest stworzenie systemu OWL (Object-Oriented Workplace Laboratory)
jako próby usprawnienia sposobu budowania biurowców.

2. Scenariusze

2.1. Sterowanie funkcjami budynku

Pracownicy dokonują dostosowywania swego środowiska pracy do własnych potrzeb za pomocą


osobistego modułu środowiskowego (PEM — Personal Environment Module), którego interfejsem
jest przeglądarka W W W . Moduł ten sprawuje kontrolę nad aktualnym stanem środowiska, rejestrując
w swej bazie akcje poszczególnych pracowników i regulując odpowiednio ogrzewanie i wentylację
miejsca pracy. Moduł PEM współpracuje z modułami PEM sąsiadujących stanowisk w celu spraw-
dzenia, czy chłodzenie danego stanowiska nie wymaga dogrzewania stanowisk sąsiednich.

[...]
2.5. Utrzymanie i konserwacja budynku

System monitoruje zachowanie kontrolowanych urządzeń w celu wykrywania ewentualnych usterek.


Zdarzenia w rodzaju przepalenia żarówki czy odczytu nienormalnych wartości parametrów rapor-
towane są personelowi zarządzającemu, odpowiedzialnemu za konserwację sprzętu i jego inspekcje.
Częstotliwość występowania awarii poszczególnych urządzeń jest analizowana pod kątem regular-
ności, na podstawie której personel może zawczasu podjąć środki uprzedzające kolejne awarie.

[...]
3.3. Koncepcje organizacyjne projektu 131

Dokument deklaracji problemu nie zawiera kompletnej specyfikacji systemu, ogranicza


się jedynie do wstępnego określenia aktywności ustanawiającej platformę porozumienia mię-
dzy klientem a zespołem projektu. Aktywnościami związanymi ze zbieraniem wymagań od
klienta zajmiemy się w rozdziałach 4. „Zbieranie wymagań" i 5. „Analiza wymagań".

Przegląd kliencki

Celem przeglądu klienckiego jest umożliwienie klientowi oceny postępu prac prowa-
dzonych przez programistów, a także dostarczenie programistom potwierdzenia wymagań
lub żądania zmian pod adresem tworzonego systemu. Przegląd kliencki stanowi więc okazję do
potwierdzenia oczekiwań z obu stron i budowania coraz lepszego porozumienia między
uczestnikami projektu. Przedmiotem zainteresowania przeglądu klienckiego są elementy funk-
cjonalne systemu i ewentualne ograniczenia istotne dla klienta (wydajność, platforma sprzęto-
wa oraz systemowa i tym podobne). W większości przypadków pomijane są kwestie związane
z projektowaniem i implementowaniem systemu, chyba że są interesujące (bo nieobojętne)
dla ldienta lub użytkownika — wyjątki dotyczą najczęściej kwestii bezpieczeństwa lub ograniczeń
o charakterze prawnym.
Przegląd kliencki przeprowadzany jest jako formalna prezentacja, w czasie której
programiści skupiają się na specyficznym aspekcie funkcjonalności z perspektywy klienta.
Przegląd kliencki poprzedzany jest emisją produktu — dokumentu specyfikacji, makiety in-
terfejsu czy ewaluacyjnego prototypu podsystemu. W odpowiedzi na wspomnianą prezentację
klient przekazuje swoje uwagi programistom; uwagi te mogą sprowadzać się do generalnej
aprobaty zaprezentowanych rozwiązań bądź też mogą zawierać wymogi lub sugestie w zakresie
funkcjonalności systemu lub harmonogramu. W tabeli 3.4 widoczny jest przykład agendy
przeglądu klienckiego.

Tabela 3.4. Przykładowa agenda przeglądu klienckiego

Agenda testów akceptacyjnych klienta OWL


Data 5 grudnia
Czas 15:00-16:30

Lokalizacja Forest Hall


Cel Ocena systemu przez klienta i identyfikacja niezałatwionych problemów

Program Deklaracja problemu


Cele projektu
Architektura systemu
Demo nr 1: Interfejs zdalnego użytkownika i zdalna kontrola
Demo nr 2: Edytor struktury stanowisk pracy
Demo nr 3: Trójwymiarowa wizualizacja i omówienie interfejsu użytkownika
Pytania i odpowiedzi
Podsumowanie
132 Rozdziat 3. • Organizacja projektu i komunikacja

Przegląd projektowy

Cele przeglądu projektowego są dwojakie: dla menedżera projektu jest on okazją do


oszacowania statusu projektu, dla zespołów programistów to czas na wzajemną prezentację
interfejsów poszczególnych podsystemów. W czasie przeglądu projektu programiści mają też
sposobność do wymiany opinii na temat wspólnych problemów dotyczących systemu lub
używanych narzędzi. Meritum przeglądu projektowego zależne jest od produktu docelowego,
który jest podmiotem przeglądu: jeśli produktem tym jest projekt systemu, zainteresowanie
skupia się na dekompozycji i interfejsach poszczególnych podsystemów, w przypadku projektu
obiektów przedmiotem zainteresowania są interfejsy tychże obiektów, zaś w przypadku te-
stowania i integrowania podsystemów najistotniejsze są same testy i ich rezultaty. Przegląd
projektowy ma zwykle formę formalnej prezentacji, w ramach której poszczególne zespoły
demonstrują swoje podsystemy menedżerowi i (lub) innym zespołom zależnym od tych pod-
systemów. Przegląd poprzedzony jest zwykle stworzeniem dokumentu (na przykład doku-
mentu projektu systemu) opisującego wybrane aspekty przedmiotowego systemu (na przy-
kład interfejsy poszczególnych podsystemów). Przy zakończeniu przeglądu programiści mogą
negocjować zmiany w interfejsach i (lub) przesunięcie terminów w harmonogramie.

Przegląd partnerski

Dwie techniki, które walnie przyczynić się mogą do poprawy jakości systemu, to in-
spekcja kodu źródłowego i wędrówki po kodzie; techniki te praktykowane są w czasie prze-
glądu partnerskiego (w odróżnieniu od przeglądu projektowego czy przeglądu klienckiego).
W ramach wędrówek po kodzie programista prezentuje swoim kolegom programistom napi-
sany przez siebie kod źródłowy, wiersz po wierszu. Daje to okazję wychwycenia wielu podej-
rzanych konstrukcji i tym samym wczesnego zidentyfikowania przyczyn rozmaitych błędów.
Rolą programisty prezentera jest kierowanie prezentacją i odpowiadanie na pytania kolegów
z zespołu. Inspekcja kodu jest natomiast badaniem zgodności kodu z predefiniowaną listą kry-
teriów (na przykład sprawdzanie, czy dany fragment kodu istotnie jest implementacją kon-
kretnego algorytmu albo czy fragment kodu odwołuje się do innych podsystemów, zgodnie
ze specyfikacją ich API). Role uczestników są tu odwrotne w stosunku do wędrówki po kodzie
— tutaj zespół zadaje pytania, na które programista musi odpowiadać. Należy jednak pamiętać,
że podmiotem inspekcji oraz wędrówki po kodzie jest sam kod, a nie jego autor.
W czasie przeglądu partnerskiego wspólnym punktem odniesienia w komunikacji mię-
dzy uczestnikami jest kod źródłowy. Inspekcje i wędrówki mają ten sam cel, co przegląd pro-
jektowy — poprawę jakości kodu i rozpowszechnienie niezbędnych informacji operacyjnych
— różnią się jednak od niego mniej formalnym charakterem, ograniczonym liczebnie audyto-
rium i dłuższym zazwyczaj czasem trwania. Jak dowodzą tego liczne przykłady (między in-
nymi [Fagan, 1976]), inspekcja kodu i wędrówki po kodzie są technikami powszechnie stosowa-
nymi przez programistów, są bowiem efektywne pod względem wczesnego wykrywania usterek
kodu. Wędrówkami po kodzie zajmiemy się dokładniej w rozdziale 11. „Testowanie".
3.3. Koncepcje organizacyjne projektu 133

Przegląd statusowy

W przeciwieństwie do przeglądów klienckiego i projektowego, które dotyczą samego


systemu, przegląd statusowy koncentruje się na zadaniach związanych z systemem. Przeglądy
statusowe przeprowadzane są zwykle na forum zespołu (na przykład co tydzień), okazjonalnie
też mogą być przeprowadzane na szczeblu całego projektu (przykładowo raz na miesiąc).
Celem przeglądu statusowego jest wykrywanie odchyleń od planu realizacji zadań i korygo-
wanie tych odchyleń. Przegląd statusowy stanowi dla programistów dodatkową motywację
do szybszego zakończenia zaległych żądań: przegląd statusu zadania zachęca do dyskusji nad
otwartymi i nieprzewidzianymi wcześniej problemami, co w naturalny sposób generuje nie-
formalną komunikację między programistami. Często poszukiwanie rozwiązań wspólnych
problemów staje się efektywniejsze, gdy przeprowadzane jest wewnątrz zespołu, nie na forum
całego projektu.
Przeglądy statusowe reprezentują inwestycję w produktywność programistów: zwięk-
szenie efektywności samych przeglądów ma pozytywny wpływ na globalną efektywność ca-
łego zespołu. Przeglądy statusowe powinny odbywać się w oparciu o sporządzoną wcześniej
agendę, opisującą zadania i problemy do omówienia. Dzięki niej uczestnicy mogą lepiej przy-
gotowywać się do dyskusji, a także uzupełniać wspomnianą agendę o pilne problemy, które się
niespodziewanie pojawią. Protokoły z każdego spotkania, sporządzane przez wyznaczonego
uczestnika, powinny uwzględniać jak najwięcej informacji (głównie na temat statusu projektu
i podejmowanych decyzji) i stać się jak najszybciej dostępne dla wszystkich uczestników prze-
glądu. Z jednej strony, motywuje to odpowiedzialnego uczestnika do skrupulatnego notowa-
nia, z drugiej, daje możliwość zapoznania się ze szczegółami spotkania tym uczestnikom,
którzy byli nieobecni. Protokoły okazują się nieocenione w przypadku dyskusji nad poszcze-
gólnymi zadaniami oraz wtedy, kiedy trzeba wyjaśnić jakieś wątpliwości. Protokoły stanowią
ponadto fragment historii projektu, którą można przeanalizować po jego zakończeniu.

Burza mózgów

Celem burzy mózgów jest wygenerowanie jak największej liczby propozycji rozwiązań
problemu, niezależnie od ich rzeczywistej wartości merytorycznej, a następnie ocena każdej
z tych propozycji. Burzę mózgów urzeczywistnia się zwykle w ramach spotkań osobistych,
lecz równie dobrze można ją prowadzić za pomocą e-maili lub grup dyskusyjnych, komu-
nikatorów i tym podobnych form komunikacji. Sens burzy mózgów zasadza się na prostym
spostrzeżeniu, iż każda propozycja, choćby nawet najbardziej dziwaczna, może stać się
inspiracją dla bardziej konkretnych pomysłów. Co więcej, w przypadku problemów szczegól-
nie skomplikowanych rozwiązanie pojawić się może za pośrednictwem idei, która począt-
kowo wydawała się niedorzeczna. Istotą burzy mózgów jest myślenie poza utartymi sche-
matami. Burza mózgów ma ponadto dwa korzystne efekty uboczne: ocena pomysłów na
forum grupy skłania do formułowania bardziej czytelnych kryteriów tej oceny, a poza tym
łatwiej osiąga się wówczas konsensus dotyczący wybranego rozwiązania.
134 Rozdziat 3. • Organizacja projektu i komunikacja

Emisje

Emisja produktu, często w celu zastąpienia jego starszej wersji, wymaga udostępnienia
innym uczestnikom informacji o tym fakcie. Informacja ta może mieć formę tak prostą jak
dwulinijkowy komunikat (jak poniżej) bądź też może składać się z wielu części, na przykład
opisu nowej wersji produktu, listy zmian od jego poprzedniego wydania, listy nierozwiąza-
nych wciąż problemów i wątpliwości oraz nazwiska autora.

Od: Al
Grupy dyskusyjne: cs413.f96.architecture.discuss
Temat: SDD
Data: 2003-11-25 03:39:23
Lines: 6
Message-ID: <3299B30.3507@andrew.emu.edu>
MimeVersion: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Nowa wersja dokumentacji API dla podsystemu powiadamiania dostępna jest pod adresem
http: //decaf/~al/FRIEND/notifapi.html
—Al
Kierownik grupy powiadamiania

W ten oto sposób duża porcja informacji może być szybko rozpowszechniona w formie
kontrolowanej i spójnej — wiele zmian rozpowszechnionych zostaje łącznie, w jednym
miejscu. Przeglądy kliencki i projektowy poprzedzane są zwykle informacją o konkretnym
produkcie docelowym lub kilku produktach.
W rozdziale 13. „Zarządzanie konfiguracją" opiszemy zarządzanie wersjami dokumen-
tów, modeli i podsystemów.

Przegląd post mortem

Celem przeglądu post mortem jest wyciągnięcie wniosków z całego procesu tworzenia
systemu, który został właśnie dostarczony klientowi. Im szybciej taki przegląd zostanie prze-
prowadzony, tym mniejsze ryzyko utraty lub zniekształcenia istotnych informacji. Zakończenie
realizacji projektu to dobry moment do oceny technik, metod i narzędzi oraz wskazania tych,
które w stopniu krytycznym przyczyniły się do powodzenia (lub klęski) tej realizacji.
Przegląd post mortem może być przeprowadzany jako sesja burzy mózgów, jako wy-
wiad poparty strukturalnym kwestionariuszem bądź jako zestaw indywidualnych raportów
stworzonych przez uczestników (lub zespoły). Niezależnie od formy, konieczne jest uwzględ-
nienie wszystkich narzędzi, metod, organizacji i procedur wykorzystywanych w związku z za-
kończonym projektem.
3.3. Koncepcje organizacyjne projektu 135

W tabeli 3.5 widoczne są przykładowe pytania, jakie paść powinny w ramach przeglądu
post mortem. Nawet jeśli rezultaty przeglądu post mortem nie zostaną rozpowszechnione
oficjalnymi kanałami (na przykład w formie raportów technicznych), mogą być przekazywane
pośrednio uczestnikom projektu. Ci bardzo często przydzielani są do kolejnych projektów czy
oddelegowani do pełnienia innych funkcji, mają więc okazję upowszechniać wewnątrz firmy
wnioski z lekcji wyciągniętych przy realizacji poprzednich projektów. Przegląd post mortem
jest zatem idealną okazją do wszelkich podsumowań związanych z ostatnio zakończonymi
(lub zaniechanymi) projektami.

Tabela 3.5. Przykłady pytań w ramach przeglądu post mortem

Napotkane problemy Jakie rodzaje k o m u n i k o w a n i a się i negocjowania miały


miejsce w związku z tworzeniem systemu?
Możliwe sposoby rozwiązywania Jaki r o d z a j s t r u k t u r y i n f o r m a c y j n e j byłby o d p o w i e d n i
problemów dla pracy zespołowej w połączeniu z obiektową metodologią
tworzenia oprogramowania?

Czy uważasz, że dostępne formy komunikacji (dokumenty,


dyskusje, ogłoszenia) potrafią sprostać temu wyzwaniu? Jakie
widzisz problemy w ramach istniejącej struktury informacyjnej
i w jaki sposób chciałbyś je rozwiązywać?
Inne aspekty projektu Jakie inne wnioski, obserwacje i k o m e n t a r z e dotyczące
— sprawdzone lub wymagające zakończonego projektu nasuwają ci się w związku z:
usprawnienia • twoimi oczekiwaniami przy r o z p o c z y n a n i u p r o j e k t u
i ich ewolucją w trakcie jego realizacji,

• celami projektu,
• realnością uwzględnionych przypadków użycia,

• cyklem życiowym projektu,


• zarządzaniem p r o j e k t e m i wynikającymi z niego
spotkaniami, k o m u n i k a c j ą i tym p o d o b n y m
dokumentowaniem projektu.
Inne kwestie Jakie inne p r o b l e m y dostrzegasz w związku z realizacją
projektu i jakie z a p r o p o n o w a ł b y ś sposoby ich
rozwiązywania?

3.4.2. Komunikacja pozaplanowa


W idealnym przypadku komunikacja w ramach projektu odbywa się wyłącznie w związku
z przewidywalnymi zdarzeniami. W praktyce jednak trudno przewidywać a priori wszelkie
zdarzenia i planować z wyprzedzeniem niezbędną komunikację, o czym niech zaświadczy
choćby poniższy przykład.
136 Rozdziat 3. • Organizacja projektu i komunikacja

Niedziela, 29 marca 1998. Uczestnicy projektu JAMES1 gorączkowo przygotowują się do dostarczenia
klientowi ukończonego systemu. Rozpoczęcie testów akceptacyjnych uzgodnione zostało z klientem
na 31 marca, na godzinę 15:00 środkowoeuropejskiego czasu letniego. Z powodu przejścia na czas
letni w nocy z 28 na 29 marca programistom ubyła jedna godzina. Testy akceptacyjne prowadzone
będą w formie trójstronnej wideokonferencji między klientem w Stuttgarcie, niemieckimi progra-
mistami z Technical University Munich (TUM) w Monachium i amerykańskimi programistami
z Carnegie Mellon University (CMU) w Pittsburgu. Amerykanie mają rozpocząć konferencję o godz.
9:00 swojego czasu. Gotowa jest agenda — każdy zespół ma 12 minut na zaprezentowanie funkcjo-
nalności swojego podsystemu.

Późnym wieczorem. Niemieccy programiści dowiadują się nagle, niejako przypadkiem, od przeby-
wającego u nich studenta-programisty z CMU, że w USA przejście na czas letni dokonuje się o ty-
dzień później niż w Europie, wobec czego różnica czasu między Monachium a Pittsburgiem wynosi
obecnie 7, nie 6 godzin, a zatem Amerykanie powinni rozpocząć wideokonferencję o godzinie 8:00,
nie 9:00 swojego czasu. Niecałe 48 godzin przed rozpoczęciem testów akceptacyjnych Amerykanie
uświadamiają sobie, że o mały włos nie rozminęli się o godzinę z harmonogramem! Powiadomienie
o tym fakcie wszystkich członków amerykańskiego zespołu wymagać będzie być może całego dnia.

Powyższa historyjka jest przykładem niewłaściwego komunikowania w związku z przej-


ściem na czas letni. Z perspektywy czasu można to zakwalifikować jako poważne przeoczenie
— tylko przez przypadek nie doszło do katastrofy. Cóż, w ferworze przygotowań każdy
z uczestników projektu JAMES myślał tylko o własnych zadaniach, w kontekście własnej lo-
kalizacji, nie biorąc pod uwagę kwestii wynikających z geograficznego rozproszenia. Gene-
ralnie, zdarzenia wynikające z (wydawałoby się) odosobnionych faktów są bardzo trudne do
przewidywania, jeśli nikt z uczestników nie prezentuje globalnego spojrzenia na ogół tych
faktów. W rezultacie należy być przygotowanym na radzenie sobie z nieprzewidywalnymi
zdarzeniami, często pod presją czasu. Do takich zdarzeń, wymagających komunikacji poza-
planowej, zaliczamy między innymi:

• żądania wyjaśnień,
• żądania zmian,
• rozwiązywanie problemów.

Przyjrzyjmy się szczegółowo każdej z tych kategorii.

Żądanie wyjaśnień

Żądanie wyjaśnień, choć wiąże się często z intensywną komunikacją między uczest-
nikami projektu, jest zdarzeniem nieprzewidywalnym. Uczestnicy mogą żądać wyjaśnień
dotyczących dowolnego aspektu systemu, niezrozumiałego lub niejasnego dla nich. Forma
wymiany związanej z tym informacji bywa rozmaita, zależnie od środków komunikowania
dostępnych dla uczestników — mogą to być e-maile, rozmowy telefoniczne, nieformalne spo-
tkania i tym podobne. Oto przykład żądania wyjaśnień za pomocą postu grupy dyskusyjnej:

1
Patrz sekcja 16.5.3 — przyp. tłum.
3.3. Koncepcje organizacyjne projektu 137

Od: Alice
Grupy dyskusyjne: cs413.architecture.discuss
Temat: SDD
Data: 2010-10-10 23:12:48
Message-ID: <325DBB30.4380@andrew.cmu.edu>
MimeVersion: 1.0
Content-Type: text/plain; charset=us-ascii

Kiedy dokładnie ma być gotowy dokument projektu systemu? Zgodnie z harmonogramem


jest to 22 października, lecz mój szablon zawiera datę 7 listopada.
Dziękuję,
Alice

W tym miejscu należy zauważyć, że sytuacja, w której większość informacji związanej


z projektem pozyskiwana jest w ten właśnie sposób, jest sytuacją patologiczną, świadczącą
o wadliwej infrastrukturze komunikacyjnej. W konsekwencji jest to poważne zagrożenie dla
projektu, wynikające z nieporozumień lub nawet gubienia istotnych informacji.

Żądanie zmian

Za pomocą żądania zmian uczestnicy projektu mogą zgłaszać zauważone problemy i,


opcjonalnie, proponować sposoby ich rozwiązywania. Problemy te mogą dotyczyć zarówno
samego systemu, jak i jego dokumentacji, procesu realizacji czy organizacji projektu. W przy-
padku dużego projektu i (lub) licznych zespołów żądania zmian często mają postać sformali-
zowaną, obejmującą zwykle klasyfikację problemu (poważna usterka, żądanie nowego ele-
mentu funkcjonalnego, komentarz), opis problemu i kontekstu, w którym wystąpił, oraz
wskazanie dodatkowych faktów i materiałów. Przykład sformalizowanego żądania zmian wi-
doczny jest w tabeli 3.6.
Formularze podobne do przedstawionego w tabeli 3.6 zyskały sobie dużą popularność
za sprawą upowszechnienia aplikacji wspomagających śledzenie usterek w oprogramowaniu,
mogą być jednak z powodzeniem wykorzystywane do celów niezwiązanych bezpośrednio
z samym tworzeniem kodu, czyli podczas planowania zadań, projektowania systemu czy pla-
nowania testów i testowania.

Rozwiązywanie problemów

Gdy rozpoznany zostanie problem i ocenione proponowane sposoby jego rozwiązania,


należy wybrać jedno z rozwiązań, zakomunikować fakt tego wyboru i wybrane rozwiązanie
zaimplementować. W małych organizacjach może się to odbywać na przykład w ramach burzy
mózgów; w organizacji hierarchicznej, a także w sytuacjach kryzysowych, wspomnianego wyboru
dokonuje jedna osoba, narzucając innym uczestnikom swą decyzję. Tak czy inaczej, wybrane
rozwiązanie należy udokumentować i uczynić wiadomym dla wszystkich zainteresowanych
138 Rozdziat 3. • Organizacja projektu i komunikacja

Tabela 3.6. Przykład formularza formalizującego żądanie zmiany

Nagłówek identyfikujący żądanie Raport nr: 1291


Data: 3 maja 2010
Autor: Dave
Krótka charakterystyka problemu: Aplikacja kliencka systemu
FRIEND załamuje się w przypadku wysłania pustego formularza
Informacja kontekstowa pomocna Podsystem: Interfejs użytkownika, wersja 3.4.1
w zlokalizowaniu problemu Klasyfikacja:
• brakujący/niepoprawny element funkcjonalny
• naruszenie konwencji
• błąd programu
• błąd w dokumentacji
Istotność błędu:
• poważny
• umiarkowany
• irytujący
Opis problemu i uzasadnienie Opis:....
zastrzeżeń Uzasadnienie:...
Opis proponowanych zmian:

uczestników — dokumentacja taka stanowić będzie miarodajny punkt odniesienia w przy-


padku ewentualnych nieporozumień w późniejszych fazach projektu, poza tym efektywne ko-
munikowanie decyzji pozwala na lepszą synchronizację działań poszczególnych uczestników.
Baza, w której przechowywana jest dokumentacja pojawiających się problemów, staje
się naturalnym mechanizmem komunikacyjnym, wspomagającym rozwiązywanie problemów.
Na rysunku 3.13 widoczny jest ekran ukazujący fragment przykładowej bazy komunikatów.
Tytuł każdego komunikatu poprzedzony jest przedrostkiem wskazującym na jego charakter:
I: oznacza problem {Issue), P: — propozycję (Proposal), zaś A+ i A- to przedrostki identyfi-
kujące argumenty (Arguments) odpowiednio „za" i „przeciw" prezentowanej propozycji. Gdy
dany problem zostaje ostatecznie rozwiązany, fakt ten znajduje odzwierciedlenie w postaci
pojedynczego komentarza, opatrywanego przedrostkiem R: (Resolution).
Bazom problemów do rozwiązania i modelowaniu samych problemów poświęcimy roz-
dział 12. „Zarządzanie racjonalizacją".

3.4.3. Mechanizmy komunikacyjne


Mianem mechanizmu komunikacyjnego określamy narzędzie łub procedurę, które mogą być
użyte do wysyłania i odbierania informacji oraz wspomagania zdarzeń komunikacyjnych.
Mechanizmami komunikacyjnymi są więc i faks, i sygnały dymne. Mechanizm komunikacyjny
nazywamy synchronicznym, jeśli zarówno nadawca, jak i odbiorca komunikatu muszą być
przygotowani na jego transmisję; w przeciwnym razie mechanizm komunikacyjny nazywamy
mechanizmem asynchronicznym. Sygnały dymne są przykładem komunikacji synchronicz-
nej, faks jest natomiast narzędziem komunikacji asynchronicznej.
3.3. Koncepcje organizacyjne projektu 139

Rysunek 3.13. Przykład bazy problemów (Domino Lotus Notes)

Oba mechanizmy — synchroniczny i asynchroniczny — mogą być używane do celów ko-


munikacji planowej. W przykładzie z rysunku 3.14 do komunikowania się w ramach przeglądu
klienckiego można wykorzystywać zarówno faks, jak i sygnały dymne; komunikację pozaplanową
można realizować jednak wyłącznie na bazie mechanizmów synchronicznych — próba
raportowania problemu za pomocą sygnałów dymnych mogłaby po prostu zostać niezauważona
przez adresata. Warto tu zauważyć, że pojedyncza aktywność komunikacyjna może być realizo-
wana za pomocą kilku różnych mechanizmów komunikacyjnych: dokument z analizy wymagań
można przesłać faksem do klienta, który wyrazić może swoje uwagi za pomocą sygnałów dym-
nych (bo w jego firmie właśnie trwa wymiana sieci elektrycznej). Podobnie pojedynczy mecha-
nizm obsługiwać może wiele zdarzeń komunikacyjnych — przy użyciu faksu może odbierać
zarówno powiadomienia o problemach, jak i komentarze z ostatniego przeglądu klienckiego.

Rysunek 3.14. Przykłady mechanizmów komunikacyjnych. Komunikacja planowa może być reali-
zowana zarówno przez mechanizmy synchroniczne, jak i asynchroniczne; komunikacja pozaplano-
wa może być realizowana jedynie za pomocą środków synchronicznych

W tabeli 3.7 przedstawiamy synchroniczne mechanizmy komunikacyjne opisywane


w tym rozdziale; tabela 3.8 zawiera natomiast zestawienie mechanizmów asynchronicznych.
140 Rozdziat 3. • Organizacja projektu i komunikacja

Tabela 3.7. Przykłady synchronicznych mechanizmów komunikacyjnych

Mechanizm Obsługiwane zdarzenia komunikacyjne

Konwersacja okazjonalna Żądanie wyjaśnień


Żądanie zmian
Kwestionariusze i strukturalizowane wywiady Definiowanie problemu
Przegląd post mortem
Spotkania osobiste, rozmowy telefoniczne, Definiowanie problemu
wideokonferencje Przegląd kliencki
Przegląd projektowy
Przegląd partnerski
Przegląd statusowy
Przegląd post mortem
Burza mózgów
Rozwiązywanie problemów

Synchroniczna komunikacja grupowa Przegląd kliencki


(komunikatory) Przegląd projektowy
Przegląd partnerski
Burza mózgów
Rozwiązywanie problemów

Tabela 3.8. Przykłady asynchronicznych mechanizmów komunikacyjnych

Mechanizm Obsługiwane zdarzenia komunikacyjne

E-mail Żądanie zmian


Burza mózgów
Grupy dyskusyjne Żądanie zmian
Burza mózgów
WWW Informacja o emisji
Asynchroniczny przegląd partnerski
Żądanie zmian
Burza mózgów

Lotus Notes Informacja o emisji


Asynchroniczny przegląd partnerski
Żądanie zmian
Burza mózgów

Konwersacje okazjonalne

Konwersacja okazjonalna stanowi nieplanowaną, nieformalną wymianę informacji,


b a z u j ą c ą w zasadzie n a p r z y p a d k u — uczestnicy w y k o r z y s t u j ą okazję d o konwersacji. W ra-
m a c h takiej w ł a ś n i e k o n w e r s a c j i w y k r y t y został o p i s a n y w c z e ś n i e j p r o b l e m ze z m i a n ą czasu.
O t o kolejny przykład.
3.3. Koncepcje organizacyjne projektu 141

Sally i Bob, uczestniczący w tym samym projekcie, spotkali się przy automacie z kawą. Sally, odpo-
wiedzialna jest za interfejs użytkownika. Wiedząc, że Bob jest członkiem zespołu powiadamiania,
odpowiedzialnym za komunikację między podsystemami klienckimi a serwerem, informuje go
o nieregularnych kłopotach z pobieraniem pakietów z serwera: nie jest pewna, czy przyczyna tych
kłopotów nie leży przypadkiem w jej kodzie. I dowiaduje się od Boba, iż równolegle z jej próbami
pobierania pakietów testowana była nowa rewizja systemu komunikacyjnego; Bob, w trosce o oszczęd-
ność czasu, pominął obowiązujące w takich przypadkach oficjalne procedury związane z zarządza-
niem konfiguracją. Ostatecznie wyjaśnia to całą zagadkę.

Takie okazjonalne rozmowy mają zwykle znaczący udział w całościowej komunikacji


związanej z projektem. Nic nie kosztują, a bywają niesamowicie skuteczne podczas rozwiązy-
wania prostych problemów wynikających z braku dostatecznej koordynacji działań między
uczestnikami. Przyczyniają się także do wymiany wiedzy operacyjnej na temat powszechnie
znanych problemów i wątpliwości, używanych narzędzi i procedur oraz źródeł informacji na
temat projektu. Ich słabą stroną jest niewielka zazwyczaj liczba rozmówców i brak dokumen-
towania historii: wiele ważnych informacji może zostać przez to zagubionych, a informacja
rozpowszechniana jedynie w sposób werbalny podatna jest na zniekształcenia. Co więcej, po
tym werbalnym przekazie nie pozostaje żaden trwały ślad w postaci dokumentu, bazy danych
czy choćby e-maili — ślad, który mógłby stanowić punkt odniesienia do analizy decyzji po-
dejmowanych i oznajmianych w trakcie takich konwersacji.

Kwestionariusze i wywiady strukturalne

Celem kwestionariusza jest zebranie informacji od jednej lub kilku osób w sposób
strukturalny. Kwestionariusze stosowane są zazwyczaj do pozyskiwania wiedzy od użytkow-
ników i ekspertów, w celu lepszego zrozumienia wymagań i priorytetów klienta, mogą być też
używane na potrzeby wyciągania wniosków z przeglądu post mortem. Kwestionariusz może
zawierać zarówno pytania o charakterze otwartym, jak i opcje wyboru (być może wielokrotne-
go). Kwestionariusze mają tę zaletę, że pozyskiwanie informacji odbywa się przy minimalnym
fatygowaniu użytkownika. Oczywiście, każdy kwestionariusz musi być sporządzony indywidual-
nie, niezależnie od pozostałych, a wszelkie niejasności lub niekompletne odpowiedzi powinny
zostać wyjaśnione w ramach wywiadu strukturalnego. Podstawową wadą kwestionariuszy jest
fakt, że trudno je właściwie projektować; wysiłek włożony w ich zaprojektowanie jest jednak
opłacalny, jeśli weźmie się pod uwagę potencjalne konsekwencje niedostatecznego zrozumienia
potrzeb klienta przez programistów. Projektowanie kwestionariuszy i wywiadów strukturalnych
omówione jest między innymi w książce J. T. T. Barone'a i J. Switzera [Barone i Switzer, 1995].

Spotkania

Spotkania osobiste umożliwiają niewielkim grupom uczestników wymianą, ocenę i ne-


gocjowanie problemów oraz rozwiązań. Dotąd nie wymyślono innego, ale równie skutecznego
sposobu znajdowania dobrych rozwiązań i budowania konsensusu. Organizacja zebrania wy-
maga jednak zainwestowania pewnych kosztów i pozyskania zasobów, ponadto zebraniami
trudno zarządzać, gdyż ich przebieg łatwo wymyka się spod kontroli. Aby więc sprawić, by ze-
brania stały się jak najbardziej treściwe i efektywne pod względem liczby podjętych decyzji,
zwykle wybranym uczestnikom przypisuje się trzy poniższe role.
142 Rozdziat 3. • Organizacja projektu i komunikacja

• Facilitator — odpowiedzialny jest za organizację zebrania i jego przebieg. Jego za-


daniem jest sporządzenie agendy opisującej cel zebrania i zakres przewidywanych
tematów; agenda powinna być gotowa na tyle wcześnie, by uczestnicy zdążyli się
z nią zapoznać przed rozpoczęciem zebrania, dzięki czemu będą mogli ocenić, jak
ważne jest dla nich samo zebranie, i przygotować ewentualne materiały pomocnicze.
• Protokolant — jego zadaniem jest dokumentowanie przebiegu zebrania, w sposób
tradycyjny (na papierze) lub za pomocą komputera; po zakończeniu zebrania ma-
teriał dokumentujący powinien zostać jak najszybciej opracowany i przekazany po-
szczególnych uczestnikom, którzy tym samym będą mogli przeanalizować swoje
zadania i role w kontekście tematyki poruszanej na zebraniu. Dokumentacja zebrania
może też być użyteczna dla tych uczestników, którzy nie byli na nim obecni.
• Chronometrażysta — kontroluje zgodność wykorzystywania czasu zebrania z agendą,
informując facilitatora o przekroczeniu założonego czasu dyskusji na dany temat.

Agenda spotkania składa się z przynajmniej trzech części: nagłówka określającego pla-
nowane miejsce i czas spotkania, listy tematów zgłoszonych przez uczestników oraz listy pro-
blemów, dla których w trakcie zebrania poszukiwać się będzie rozwiązań. Każdemu elemen-
towi zebrania przydziela się maksymalny czas, dzięki czemu chronometrażysta może zapewnić,
że całe zebranie zakończy się w wyznaczonym terminie. W tabeli 3.9 widzimy przykład agendy;
agenda przedstawiona w tabeli 3.10, mimo pozorów poprawności, jest w istocie bezwartościowa
(załączone komentarze wyjaśniają, dlaczego).

Tabela 3.9. Przykładowa agenda zebrania

Nagłówek identyfikujący Gdzie i kiedy Role


zebranie Data: 30 stycznia 2010 Facilitator: Peter
Początek: 16:30 C h r o n o m e t r a ż y s t a : Dave
Koniec: 17:30 P r o t o k o l a n t : Ed
Pokój: W H , 3420
Oczekiwane rezultaty 1. Cel
zebrania Rozwiązanie problemów z wymaganiami klienta, uniemożliwiających
rozpoczęcie prototypowania.
Akcje podlegające 2. Status [Czas: 15 m i n u t ]
sprawozdaniu Dave: Stan kodu parsera poleceń.
Problemy przeznaczone 3. Tematy do dyskusji [Czas: 35 minut]
do przedyskutowania 3.1. Jak radzić sobie z arbitralnie s f o r m a t o w a n y m i plikami
na zebraniu danych wejściowych?
3.2. W jakiej formie tworzyć wyniki parsingu?
3.3. Szczegóły k o d u parsera (łatwość modyfikacji,
kompatybilność wstecz).
Czas podsumowania jest taki 4. Podsumowanie [Czas: 5 minut]
sam dla wszystkich zebrań 4.1. Analiza bieżących akcji i e w e n t u a l n e przydzielenie
nowych zadań.
4.2. Komentarze i wnioski.
3.3. Koncepcje organizacyjne projektu 143

Tabela 3.10. Mniej fortunny przykład agendy zebrania

Zebrania bez określonego Gdzie i kiedy Role


czasu trwania maję tendencję Data: 30 stycznia 2010 Facilitator: Peter
do przeciągania się Początek: 16:30 Chronometrażysta: Dave
w nieskończoność Koniec: Nieznany Protokolant: Ed
Pokój: W H , 3420

Cel trudny do osiągnięcia 1. Cel


i zweryfikowania Rozwiązanie zaistniałych problemów.

Brak konkretów: jakie zadania 2. Status [Czas: 15 minut]


przydzielono Dave'owi? Dave: Zadanie przydzielone Dave'owi.

Brak konkretów: jakie problemy 3. Tematy do dyskusji [Czas: 35 minut]


aktualnie istnieją w każdej 3.1. Problemy z wymaganiami klienta.
z trzech wymienionych 3.2. Problemy z projektowaniem.
kategorii?
3.3. Problemy implementacyjne.

4. Podsumowanie [Czas: 5 minut]


4.1. Analiza bieżących akcji i ewentualne przydzielenie
nowych zadań.
4.2. Komentarze i wnioski.

Protokół zebrania składać się powinien z trzech części, odpowiadających poszczególnym


częściom agendy. Dodatkowo protokół zawiera część opisującą akcje, które uczestnicy ze-
brania mają wykonać w rezultacie podjętych na zebraniu decyzji. Nagłówek protokołu określa
miejsce, czas i uczestników zebrania; w drugiej części reprezentowana jest informacja przed-
stawiona w ramach zebrania, zaś część trzecia poświęcona jest decyzjom — tym podjętym i tym,
które mimo oczekiwań nie zostały podjęte. Przykładowy protokół zebrania widoczny jest
w tabeli 3.11.
Mimo iż zebranie odbywające się w jednym miejscu jest najbardziej efektywne, możliwe
jest także organizowanie zebrań uczestników rozproszonych geograficznie, za pomocą tele-
konferencji lub wideokonferencji. Takie rozproszone zebrania bywają bardziej oszczędne, gdy
chodzi o koszty i zasoby, lecz także mniej efektywne i mniej niezawodne, z powodu zawodności
samych środków (internet) służących do ich realizacji. W warunkach ograniczonej jakości
dźwięku i obrazu wyjątkowego znaczenia nabiera dobrze przygotowana agenda; możliwość
rozróżniania głosu (i innych unikalnych cech osobowych) poszczególnych uczestników przy-
czynia się wydatnie do lepszej komunikacji.
Facilitator sporządzający agendę powinien być tak konkretny, jak to tylko możliwe, by
uniknąć niepotrzebnego jej rozbudowywania. Istnieje pokusa ułatwiania sobie pracy poprzez
sporządzanie szablonowych, zbyt ogólnych agend, w rodzaju widocznej w tabeli 3.10, przy-
datnych dla każdego zebrania bez większych zmian. Agenda przestaje być wówczas czynni-
kiem wspomagających efektywność zebrania, a staje się niepotrzebną procedurą biurokra-
tyczną, niedającą uczestnikom żadnych korzyści.
144 Rozdziat 3. • Organizacja projektu i komunikacja

Tabela 3.11. Przykładowy protokół z zebrania

Nagłówek identyfikujący Gdzie i kiedy Role


zebranie i jego audytorium Data: 30 stycznia 2010 Facilitator: Peter
Początek: 16:30 Chronometrażysta: Dave
Koniec: 18:00 Protokolant: Ed
Pokój: W H , 3420 Obecni: Ed, Dave, Mary, Peter, Alice

Taki sam jak w agendzie 1. Cel

Streszczenie prezentowanej Status


informacji

Dyskutowane tematy 3. Dyskusja


i przedstawione rozwiązania 3.1. Kod parsera to instrukcja if o rozmiarze 1200 - 1300 wierszy.
W y d a j e się wręcz n i e w y k o n a l n e d o d a n i e nowych poleceń
lub m o d y f i k o w a n i e znaczenia istniejących bez naruszenia
kompatybilności z dotychczasową wersją wykorzystywaną
przez klientów.
Propozycje rozwiązania:
1) Restrukturyzacja kodu parsera przez przyporządkowanie
o d r ę b n e g o obiektu k a ż d e m u rodzajowi poleceń.
2) Identyfikowanie poszczególnych parametrów polecenia przez
nazwę, co z jednej strony, ułatwi w przyszłości zachowanie
kompatybilności wstecz, z drugiej jednak, spowoduje zwiększenie
rozmiaru polecenia i plików zawierających polecenia.
Przyjęte rozwiązanie:
Restrukturyzacja kodu. Kompatybilność wstecz nie jest obecnie
poważnym problemem, gdyż kod wywołujący parser i tak musi
zostać napisany na nowo. Patrz AI[1].

Uzupełnienie i modyfikacja 4. Podsumowanie [Czas: 5 minut]


istniejącego planu zadań AI[1] Dla: Dave.
Przeróbka kodu parsera ze szczególnym uwzględnieniem jego
modularności, koordynacja działań z Billem z grupy baz danych
(która być może wymagać będzie zachowania kompatybilności wstecz).

Narzędzia komunikacji grupowej

O p r o g r a m o w a n i e w s p o m a g a j ą c e dyskusję r o z p r o s z o n y c h u c z e s t n i k ó w w czasie rze-


c z y w i s t y m p r z e z długi czas b y ł o d o m e n ą o ś r o d k ó w a k a d e m i c k i c h ( [ G r u d i n , 1988], [Borghoff
i Schlichter, 2000]), lecz z b i e g i e m czasu zyskało p o p u l a r n o ś ć t a k ż e w k r ę g a c h b i z n e s o w y c h za
s p r a w ą czatu i n t e r n e t o w e g o . N a r z ę d z i a w r o d z a j u N e t m e e t i n g [Microsoft] u m o ż l i w i a j ą uczest-
n i k o m w s p ó ł p r a c ę g r u p o w ą w e w s p ó ł d z i e l o n e j przestrzeni. W s p ó ł p r a c a ta o p i e r a się n a m e -
taforze rzeczywistych spotkań: użytkownik „wchodzi" do pokoju, w k t ó r y m m o ż e oglądać
t e k s t l u b grafikę b ę d ą c ą p r z e d m i o t e m dyskusji. W s z y s c y u ż y t k o w n i c y są r ó w n o u p r a w n i e n i ,
3.3. Koncepcje organizacyjne projektu 145

choć w danej chwili tylko jeden może modyfikować ów przedmiot dyskusji: zabieranie głosu
może mieć charakter anarchiczny (wszyscy wypowiadają się w sposób niekontrolowany) bądź
sekwencyjny (użytkownik mający głos nie może być z niego wywłaszczony, dopóki sam nie
przekaże go kolejnemu dyskutantowi).
Słabą stroną takich „synchronicznych" dyskusji jest trudność koordynowania poczynań
poszczególnych dyskutantów. Pisanie na klawiaturze jest trudniejsze (wolniejsze) od swobod-
nego wypowiadania się (dla typowego, niezaprawionego w czatowaniu użytkownika), poza
tym zredukowanie formy wymiany informacji do tekstu pisanego stwarza ryzyko zagubienia
wielu informacji, które w rozmowie bezpośredniej można wyrazić na przykład przez gesty-
kulację. Co więcej, drobne nawet zakłócenia w działaniu sieci (internetu) mogą powodo-
wać interferencję działań grożącą zupełną utratą koordynacji. I mimo iż dostępna przepusto-
wość internetu zwiększa się nieustannie, a na rynku dostępne są znakomite narzędzia do
obsługi internetowych wideokonferencji — takie jak Lotus Sametime [Lotus] — narzędziom
tym jeszcze daleko do dojrzałości wymaganej w związku z niezawodnością codziennej pracy.
W konsekwencji praca grupowa w rozproszeniu wciąż pozostaje dla projektantów i pro-
gramistów niebagatelnym wyzwaniem, któremu stawić czoła można jedynie poprzez staranne
jej planowanie z odpowiednim wyprzedzeniem. Grupowe opracowywanie procedur wspoma-
gających pracę grupową to kolejne wyzwanie w sytuacji, gdy kontakt bezpośredni oraz niewer-
balne kanały komunikacji nie są dostępne.
Na przestrzeni ostatnich dwudziestu lat coraz większą popularność zdobywa sobie asyn-
chroniczna komunikacja grupowa. W swej najprostszej formie stanowi ona analogię tablicy,
na której uczestnicy mogą zapisywać swoje komentarze, w podziale na wątki dedykowane
poszczególnym tematom. Sieć W W W zapewnia ponadto szeroki dostęp do ogromnych repo-
zytoriów dokumentów pochodzących z rozmaitych źródeł. Obecnie w każdym projekcie an-
gażującym więcej niż tylko garstkę uczestników w powszechnym użyciu są rozmaite narzędzia
i procedury stanowiące kombinacje forów dyskusyjnych, repozytoriów, kalendarzy i książek
adresowych, w rozmaitej skali — od darmowych narzędzi dedykowanych małym projektom
do komercyjnych rozwiązań wspomagających olbrzymią organizację. I tak na przykład dar-
mowe Yahoo Groups [Yahoo] stwarza zespołowi platformę wymiany plików i fotografii, pla-
nowania zdarzeń, umieszczania ogłoszeń i dyskutowania problemów. BSCW (Basic Support
for Cooperative Work) [BSCW] także umożliwia współpracę za pośrednictwem WWW, w po-
staci wysyłania (uploadu) dokumentów, powiadamiania o zdarzeniach i zarządzania pracą
zespołu. Wiki [Wiki] to inny przykład prostej, lecz użytecznej platformy współpracy na bazie
WWW; interesującą osobliwością Wiki jest możliwość edycji stron W W W przez każdego
użytkownika, wspomaganej automatycznym tworzeniem hiperłączy między poszczególnymi
stronami.
Wśród narzędzi komercyjnych Sharepoint Team Services [Microsoft] zawiera mechani-
zmy współpracy dla użytkowników pakietu Microsoft Office. Narzędzia wspierające przepływ
działań, takie jak Lotus Notes [Lotus], dostarczają mechanizmy replikacji, obsługi repozyto-
riów i książek adresowych upubliczniających strukturę organizacyjną. Powtarzane regularnie
czynności mogą być automatyzowane. Sformalizowane aktywności, jak asynchroniczny prze-
gląd dokumentu przez wielu uczestników, wspomagane są adnotacjami, którymi opatrywać
można dokument na jego drodze poprzez potok użytkowników.
146 Rozdziat 3. • Organizacja projektu i komunikacja

3.5. Aktywności organizacyjne


Zajmiemy się teraz aktywnościami programisty dołączającego do organizacji projektu i jego
infrastruktury komunikacyjnej. Aktywności te obejmują:

• Dołączenie do zespołu (patrz sekcja 3.5.1),


• Dołączenie do infrastruktury komunikacyjnej (patrz sekcja 3.5.2),
• Udział w zebraniu statusowym zespołu (patrz sekcja 3.5.3),
• Organizację przeglądów klienckich i projektowych (patrz sekcja 3.5.4).

Opiszemy powyższe aktywności na podstawie przykładowego projektu, skupiając się na


realizacji nowego systemu przez wiele zespołów.

3.5.1. Dołączanie do zespołu


W fazie definiowania projektu menedżer projektu organizuje zespoły programistów dla po-
szczególnych podsystemów (zwane krótko zespołami podsystemów), wyodrębnionych w ra-
mach początkowej dekompozycji architektury systemu. Dodatkowo organizowane są zespoły
międzyfunkcyjne — między innymi architektoniczny i integracyjny — jako uzupełnienie ze-
społów zajmujących się konkretnymi podsystemami. Dla każdego systemu ustanawiany jest
jego kierownik. Kluczową aktywnością w fazie definiowania projektu staje się więc przypo-
rządkowanie programistów do poszczególnych zespołów.
Przyporządkowania tego dokonuje menedżer projektu przy udziale kierowników po-
szczególnych zespołów, kierując się umiejętnościami i zainteresowaniami programistów. Każ-
dy zespół podsystemu powinien też mianować łącznika na potrzeby komunikacji z zespołami
międzyfunkcyjnymi. Menedżer projektu i kierownicy zespołów wyznaczają też zakres nie-
zbędnych szkoleń dla uczestników i całych zespołów.
W tabeli 3.12 widoczne jest przykładowe przyporządkowanie ról członkom zespołu baz
danych w projekcie OWL.

3.5.2. Dołączanie do infrastruktury komunikacyjnej


W celu obsługi komunikacji w ramach zespołów i na forum projektu tworzone są dwa rodzaje
forów. Członkowie zespołów subskrybują wszystkie fora projektu i fora należące do ich ze-
społów. W ramach forów projektu wyróżniamy:

• Ogłoszenia. Na tym forum menedżer informuje o ważnych zdarzeniach (agendach,


emisjach i tym podobnych). Jedynie menedżer ma prawo umieszczać ogłoszenia,
inni uczestnicy mogą je tylko odczytywać (wraz z niezbędnymi dokumentami)
i komentować.
• Dyskusja. Forum to przeznaczone jest na globalne żądania wyjaśnień i żądania
zmian. Dyskusja na temat żądań ma formę argumentów i alternatywnych rozwią-
zań, przesyłanych jako komentarze do oryginalnych postów. Każdy uczestnik ma
nieograniczoną możliwość umieszczania i czytania postów na tym forum.
3.5. Aktywności organizacyjne 147

Tabela 3.12. Przydział ról dla członków zespołu baz danych projektu OWL

Potrzeby
Uczestnik Role Kwalifikacje
szkoleniowe
Alice Kierownik zespołu Zarządzanie: kierownik zespołu UML
Programowanie: C Umiejętności
Zarządzanie konfiguracją komunikacyjne
John Łącznik z zespołem Programowanie: C++ Java
architektonicznym Modelowanie: UML
Implementator

Mary Menedżer Programowanie: C++, Java Obiektowe bazy danych


konfiguracji Modelowanie: relacje między encjami Modelowanie w UML
Implementator Bazy danych: relacyjne
Zarządzanie konfiguracją
Chris Implementator Programowanie: C++, Java Modelowanie w UML
Modelowanie: relacje między encjami
Bazy danych: obiektowe
Sam Główny facilitator Programowanie: C++ Inspekcja kodu
Łącznik Testowanie: biatoskrzynkowe, Java
Tester czarnoskrzynkowe

• Problemy. Forum przeznaczone jest dla niezałatwionych problemów i ich bieżącego


statusu. Każdy uczestnik ma nieograniczoną możliwość umieszczania i czytania postów
na tym forum.
• Dokumenty. Aktualne wersje produktów docelowych (dokumenty analizy wymagań,
dokumenty projektu systemu i tym podobne) i innych wewnętrznych dokumentów
(takich jak plan zarządzania projektem) umieszczane są na tym forum. Prawo wy-
syłania postów ma tylko zespół dokumentacyjny, inni uczestnicy mogą tylko od-
czytywać dokumenty i opatrywać je notatkami.
• Wyposażenie. W ramach tego forum menedżer informuje pozostałych uczestników
o wyposażeniu w sprzęt i narzędzia do realizacji projektu i o statusie poszczególnych
elementów (własne, dzierżawione). Jedynie menedżer wyposażenia ma prawo do
umieszczania postów na tym forum.

Podobnie prezentuje się grupa forów zespołowych, z których każde jednak ograniczone jest
tylko do konkretnego zespołu. Generalnie programiści mają nieograniczoną możliwość czytania
wszystkich forów zespołowych, lecz posty umieszczać mogą tylko na forum własnego zespołu.
Kreowanie forów rozpoczyna się w momencie, gdy dekompozycja systemu staje się już
względnie stabilna. Oczywiście, wraz z tworzeniem forów definiowane są także konta dla
poszczególnych uczestników.

3.5.3. Udział w zebraniach zespołu


Ważną częścią projektu programistycznego są cotygodniowe spotkania zespołowe. Dzięki nim
członkowie poszczególnych zespołów partycypują w przeglądach statusowych, burzy mózgów
i rozwiązywaniu problemów. Organizację i przebieg takich spotkań opisaliśmy w sekcji 3.4.1.
148 Rozdziat 3. • Organizacja projektu i komunikacja

Szczególnie istotne jest pierwsze spotkanie tego typu: poszczególni członkowie zespołu n a ogół
nie znają się jeszcze osobiście i tylko nieliczni ś w i a d o m i są f o r m a l n y c h ról i p r o c e d u r obowiązują-
cych n a takich z e b r a n i a c h . K i e r o w n i c y z e s p o ł ó w m a j ą więc o k a z j ę w p r o w a d z e n i a wszystkich
c z ł o n k ó w we w s p o m n i a n e p r o c e d u r y , w y j a ś n i e n i a ich istotności i z m o t y w o w a n i a d o ich
przestrzegania. P o n i ż e j w i d o c z n a jest a g e n d a s t w o r z o n a p r z e z k i e r o w n i k a z e s p o ł u w związku
z pierwszym spotkaniem.

Gdzie i kiedy Role


Data: 9 stycznia 2010 Pierwszy facilitator: Alice
Początek: 16:30 Chronometrażysta: Dave
Koniec: 17:30 Protokolant: Ed
Budynek: Wean Hall
Pokój: 3420
1. Cel:
Zaznajomienie z rolami w zarządzaniu projektem średniej skali, w hierarchii dwustopniowej,
szczególnie:
• wyjaśnienie różnicy między rolą a osobą
• przyporządkowanie ról poszczególnym członkom
• podsumowanie zebrania
• sformułowanie zagadnień na następne zebranie
2. Status i wymiana informacji [Czas: 40 minut]
2.1. Organizacja spotkań
Podstawowe reguły:
• Aktywne słuchanie
• Aktywne uczestnictwo
• Punktualne przybycie
• Brak nieformalnych spotkań
• Respektowanie agendy
• Zgodność z wyznaczonymi ramami czasowymi
• Chęć wypracowywania konsensusu
• Swoboda pod względem kontroli procesów i reguł
Role przydzielane w związku z zebraniem:
• Główny facilitator
• Chronometrażysta
• Protokolant
• Dokumentalista
• Respektowanie agendy
[...]

3. Tematy do dyskusji [Czas: 15 minut]


3.1. Książka adresowa zespołu
3.2. Przyporządkowanie ról w związku z zebraniem
3.3. Przyporządkowanie ról w ramach zespołu
4. Podsumowanie [Czas: 5 minut]
4.1. Analiza bieżących akcji i ewentualne przydzielenie nowych zadań
4.2. Komentarze i wnioski
3.5. Aktywności organizacyjne 149

Celem pierwszego zebrania zespołu jest przeszkolenie jego członków na konkretnych


przykładach. Temu celowi służyć ma dyskusja na temat procedur, wyjaśnianie ról zespoło-
wych oraz związanych z zebraniami i dokonanie przydziału ról w ramach zespołu. Wyekspo-
nowana zostaje rola facilitatora jako odpowiedzialnego za efektywność zebrania, a nie za na-
rzucanie decyzji. Członkowie zespołu zostają poinformowani, iż każdy z nich może wejść
w rolę drugiego facilitatora, czyli zainterweniować w dyskusję, gdy wymknie się ona z ram
określonych przez agendę. Uzgadniane są wówczas kluczowe zwroty na określenie stan-
dardowych sytuacji — i tak na przykład krótkie stwierdzenie „drugi facilitator" oznacza to
samo, co „ta dyskusja wymyka się z ram agendy, wróćmy na właściwe tory", a enigmatyczne
„poziom wyżej" jest synonimem stwierdzenia „dyskusja zbytnio się rozdrabnia, ten poziom
szczegółowości nie jest teraz istotny, bo większość z obecnych nie jest nim zainteresowana".
Uczestnikom zebrania uświadamia się także znaczenie oszczędności czasu, którą można osią-
gnąć przez przeprowadzanie zebrań w sposób efektywny i skoncentrowany, dzięki czemu
uczestnicy prędzej mogą wrócić do swych głównych zadań.
Kierownicy zespołów czasami dokonują rotacji ról, przez co członkowie zespołu mają
okazję sprawdzić się w każdej roli. Sprzyja to rozwijaniu dodatkowych umiejętności i potęguje
wymianę informacji; ma jednak tę wadę, że uczestnicy nie mają okazji dojrzeć w ramach po-
szczególnych ról i wykonywać swych zadań szczególnie efektywnie. Wczesne przypisanie ról,
ich rotacja i sztywne procedury prowadzenia zebrań — to wszystko może powodować pokaźne
zamieszanie na początku realizacji projektu, lecz w dłuższej perspektywie okazuje się opłacalną
inwestycją. Doskonalone codziennie umiejętności komunikacyjne okażą się nieocenione w sytu-
acjach kryzysowych, jakie zapewne nastąpią na etapie implementowania projektu.
Każdy z zespołów odpowiedzialny jest za wyznaczenie ról związanych z zebraniem
i umieszczenie związanej z tym informacji na swym forum Ogłoszenia. Dzień przed zebra-
niem statusowym facilitator zebrania obowiązany jest umieścić na tym forum szkic agendy,
na podstawie tematyki ustalonej w podsumowaniu poprzedniego zebrania, a także na pod-
stawie zespołowego forum Problemy. Ów szkic uzupełniony zostanie przez chronometrażystę
o harmonogram czasowy, przesłany na forum Ogłoszenia w formie odpowiedzi. Inni uczest-
nicy mogą komentować agendę — wraz z jej harmonogramem czasowym — w formie od-
powiedzi.
Przypisywanie ról w związku z zebraniem, procedury jego przeprowadzania — to wszystko
może wydawać się zbędną fatygą, przynajmniej dla niewtajemniczonego programisty. Mene-
dżerowie świadomi są tego faktu, na początku projektu inwestują więc wiele czasu, by prze-
konać uczestników o czymś wręcz przeciwnym. W pierwszych tygodniach realizacji projektu
menedżerowie systematycznie przeglądają agendy i protokoły zebrań, sugerując przy okazji
rozmaite sposoby bardziej efektywnego gospodarowania czasem, zarówno facilitatorowi (na
przykład kopiowanie z dokumentu do agendy aktywnych akcji przez schowek, zamiast ich
ręcznego wpisywania), jak i chronometrażyście (który powinien przeznaczyć więcej czasy na
nierozwiązane problemy niż na dyskusję).

3.5.4. Organizacja przeglądów


Przeglądy klienckie przeprowadzane są najpierw po wyprodukowaniu dokumentu analizy,
a potem po dostarczeniu systemu. Przeglądy projektowe przeprowadzane są w związku z doku-
mentami projektu systemu oraz szczegółowego projektu obiektów i w związku z testowaniem.
150 Rozdziat 3. • Organizacja projektu i komunikacja

Przeglądy projektowe mogą być związane także z uruchamianiem „na sucho"2 systemu przed
przekazaniem go klientowi do testów akceptacyjnych. Terminy wszystkich przeglądów ustalane
są przez menedżera projektu w fazie planowania. Przykładowy harmonogram przeglądów
przedstawiony jest w tabeli 3.13.

Tabela 3.13. Przykładowy harmonogram przeglądów

Produkt docelowy (dostarczany


Przegląd Data
z tygodniowym wyprzedzeniem)
Przegląd kliencki 7. tydzień Dokument analizy wymagań

Przegląd projektu systemu 9. tydzień D o k u m e n t projektu systemu

Przegląd projektu obiektów 13. tydzień (dwie sesje) Dokument projektu obiektów
Przegląd wewnętrzny 16. tydzień Testy modułowe i integracyjne
„Suchy przebieg" przed 17. tydzień Wszystkie produkty docelowe projektu
testami akceptacyjnymi

Testy akceptacyjne 17. tydzień Wszystkie produkty docelowe projektu

Menedżerowie definiują także procedury organizowania przeglądów:

1. Produkt docelowy będący przedmiotem przeglądu powinien być dostarczony ty-


dzień'' wcześniej.
2. Krótko po dostarczeniu produktu menedżer publikuje szkic agendy zawierającej listę
szczegółów prezentacji dla każdego zespołu. Szkic ten umieszczany jest na forum
Ogłoszenia.
3. Kandydaci na prezenterów komentują szkic agendy, precyzując szczegóły prezentacji;
menedżer koryguje agendę stosownie do tych komentarzy.
4. W odpowiedzi na zaktualizowaną agendę prezenterzy wysyłają na forum slajdy
prezentacyjne; menedżer kolekcjonuje slajdy i odpowiednio uaktualnia agendę.

Menedżer ustala także obowiązki i zakres odpowiedzialności protokolanta. W czasie


przeglądu protokolant skrupulatnie rejestruje wszystkie pytania i odpowiedzi uczestników.
Następnie, jeszcze w dniu przeglądu, wraz z menedżerem łączą swe notatki i generują listę akcji
przeznaczonych do wykonania w rezultacie przeglądu oraz listę problemów, które w czasie prze-
glądu nie mogły zostać rozwiązane. Obie listy opublikowane zostają na forum Ogłoszenia.

2
Uruchamianie na sucho, suchy przebieg (ang. dry run) — mentalne „uruchamianie" programu
przez programistę, który analizując krok po kroku wykonywanie kolejnych instrukcji, obserwuje
skutki tego wykonywania. W odniesieniu do testów akceptacyjnych termin ten ma jednak trochę
inne znaczenie — odnosi się do kompletnego przetestowania systemu, zanim przekazany zostanie
klientowi do testowania. Interesująca etymologia tego terminu — w kontekście przymiotnika dry
— opisana jest pod adresem http://www.worldwidewords.org/qa/qa-dryl.htm —przyp. tłum.
Ów tydzień stanowi pewną rezerwę czasową dla opóźnionych dokumentów: zdarza się bowiem, że zo-
stają one dostarczone nawet na dzień przed planowanym przeglądem, co rodzi dwojakiego rodzaju
problemy: po pierwsze, dokumenty te muszą zostać udostępnione wszystkim uczestnikom, po drugie,
każdy uczestnik musi mieć czas na zapoznanie się z dokumentem.
3.6. Literatura uzupełniająca 151

Wymóg oficjalnego organizowania przeglądu w ramach infrastruktury komunikacyjnej


oraz udostępnienia slajdów prezentacyjnych powoduje zwiększenie ilości uwzględnianej in-
formacji i sprawia, że staje się ona dostępna dla wszystkich uczestników w dłuższej perspektywie.

3.6. Literatura uzupełniająca


Teoretycy i praktycy programowania znakomicie zdają sobie sprawę ze znaczenia sprawnej
komunikacji w realizowaniu projektów informatycznych. Gdy projekt staje się coraz większy
i coraz bardziej złożony, komunikacja staje się czynnikiem krytycznym.
Książka autorów B. Curtisa, H. Krasnera i N. Iscoa [Curtis i in., 1988] to jedna z naj-
bardziej znanych publikacji omawiających i klasyfikujących problemy komunikacyjne przy
tworzeniu oprogramowania. Z przedstawionych analiz przypadków — 17 dużych projektów
rządowych — wynika jednoznacznie, że dokumentacja wcale nie redukuje zapotrzebowania
na informację, zwłaszcza we wczesnych fazach projektu, kiedy to uczestnicy definiują pod-
stawowe pojęcia, uzgadniają konwencje reprezentacyjne i kreują nieformalne sieci wymiany
informacji. Studium to dowodzi również, iż przeszkody w nieformalnym komunikowaniu się
(wynikające na przykład z barier organizacyjnych lub rozproszenia geograficznego) mogą
prowadzić do nieporozumień w zakresie konwencji projektowych i racjonalizacji.
Obserwacje opisane w pracy R. E. Krauta i L. A. Streeter [Kraut i Streeter, 1995] do-
wodzą, że oficjalne kanały komunikowania się (zebrania, formalne specyfikacje, przeglądy
partnerskie) są użyteczne przy rutynowej koordynacji działań, podczas gdy komunikacja nie-
formalna (przypadkowe spotkania, rozmowy telefoniczne, burza mózgów) jest niezbędna
w przypadku niepewności i nieprzewidzianych problemów, czyli w sytuacjach typowych dla
tworzenia oprogramowania. Ze studium tego wynika również, że zapotrzebowanie na nie-
formalną komunikację wzrasta drastycznie wraz ze wzrostem rozmiaru i złożoności opro-
gramowania.
Jako że spotkania osobiste i wideokonferencje wciąż są podstawowym środkiem komu-
nikowania się uczestników projektu — w celu oznajmiania statusu, identyfikowania kon-
fliktów i rozwiązywania problemów — umiejętności w zakresie organizowania i przeprowa-
dzania takich spotkań są w inżynierii oprogramowania niezbędne, by spotkania te były owocne,
a istotna informacja nie była gubiona lub zniekształcana. Mimo to, edukacja w tym zakresie
rzadko stanowi ważny element ogólnie pojętej edukacji informatycznej. Wiele użytecznych
procedur, również heurystycznych, efektywnego prowadzenia zebrań opisanych jest w pra-
cach M. Doyle'a i D. Strausa [Doyle i Straus, 1982] oraz T. A. Kaysera [Kayser, 1990] — z tej
ostatniej zaczerpnęliśmy szablony przykładowych agend i protokołów prezentowanych w ni-
niejszym rozdziale.
Książka R. Fishera, W. Ury'ego i B. Pattona [Fisher i in., 1991] zawiera wyjaśnienie
podstawowych mechanizmów negocjacyjnych oraz propozycje sposobów unikania impasów
w negocjacjach.
Z książki J. T. T. Barona i J. Switzera [Barone i Switzer, 1995] dowiedzieć się można, jak
projektować kwestionariusze i przeprowadzać wywiady.
Dobrym wprowadzeniem w tematykę komunikacji grupowej i wspomagających ją
narzędzi jest książka U. W. Borghoffa i J. Schlichtera [Borghoff i Schlichter, 2000] zawierająca
wiele użytecznych porad dla twórców i użytkowników oprogramowania w zakresie tej tematyki.
152 Rozdziat 3. • Organizacja projektu i komunikacja

3.7. Ćwiczenia
3.1. Jaka jest różnica między rolą a uczestnikiem?
3.2. Czy jedna rola może być współdzielona między kilku uczestników? Dlaczego tak albo
dlaczego nie?
3.3. Jaka jest różnica między klientem a użytkownikiem?
3.4. Do jakich ról przypisałbyś poniższe zadania:
• Zmiana interfejsu podsystemu w celu uwzględnienia nowych wymagań,
• Zakomunikowanie zmiany interfejsu innym zespołom,
• Uwzględnienie zmiany interfejsu w dokumentacji,
• Zaprojektowanie zestawu testowego w celu wykrycia ewentualnych usterek wyni-
kających ze zmiany,
• Upewnienie się, że zmiana została wykonana terminowo.
3.5. Załóżmy, że jesteś odpowiedzialny za koordynację projektu systemu obsługi wnio-
sków kredytowych dla banku: w jakich rolach obsadziłbyś następujących uczestników,
by jak najlepiej wykorzystać ich kwalifikacje?
• pracownik banku, odpowiedzialny za obsługę wniosków kredytowych,
• menedżer IT banku, kontraktujący system,
• niezrzeszony programista, który w przeszłości uczestniczył w realizacji podobnych
systemów,
• dokumentalista techniczny,
• Ty.
3.6. Narysuj diagram aktywności UML reprezentujący zebranie jako proces opisany
w sekcji 3.4.1. Zwróć szczególną uwagę na produkty generowane przed zebraniem
i po nim, takie jak agenda i protokół. Wykorzystaj partycjonowanie aktywności do
wyodrębnienia poszczególnych ról.
3.7. Jaka jest różnica między produktem a pakietem? Kiedy definiowany jest pierwszy,
a kiedy drugi? Rozpatrz sytuację, gdy dwóch studentów uczestniczy w tworzeniu
podsystemu sortowania listy nazwisk, przy czym każdy student stosuje inny algo-
rytm sortowania. Oczekiwanymi produktami docelowymi są: kod źródłowy, do-
kumentacja podsystemu orz instrukcja dla innych programistów opisująca sposób
integrowania nowego algorytmu sortowania z kodem wywołującym. Podaj przykłady
pakietów i produktów związanych z tym projektem.
3.8. Jaka jest różnica między zespołem podsystemu a zespołem międzyfunkcyjnym?
3.9. Skoro przewidywanych jest wiele krytycznych zdarzeń komunikacyjnych (przegląd
kliencki, przegląd projektowy, przegląd partnerski), dlaczego istnieje ewentualność
komunikacji pozaplanowej (w związku z żądaniami wyjaśnień, żądaniami zmian,
rozwiązywaniem problemów)?
Bibliografia 153

3.10. Dla dowolnego dnia w Twoim tygodniu roboczym wymień wszystkie aktywności
kwalifikujące się jako zdarzenia komunikacyjne (koleżeńskie spotkania przy kawie,
rozmowa z zaprzyjaźnionym studentem, negocjowanie, ogłaszanie, przeglądanie
stron WWW). Jaki udział aktywności te mają w ogólnym bilansie Twoich dziennych
aktywności?
3.11. Załóżmy, że jesteś członkiem zespołu odpowiedzialnego za podsystem interfejsu
użytkownika, jesteś odpowiedzialny za zaimplementowanie formularza kolekcjonu-
jącego informację o użytkowniku (nazwisko, imię adres, e-mail, stopień doświadcze-
nia). Informacja zbierana za pomocą formularzy kolekcjonowana jest w bazie danych
i wykorzystywana przez podsystem raportowania. Nie jesteś pewien, które pola muszą
być wypełnione obowiązkowo, a które opcjonalnie — jak rozwiązałbyś ten problem?
3.12. W związku z weryfikacją planów i rotacją personelu zostałeś przeniesiony z zespołu
interfejsu użytkownika do zespołu baz danych. Implementacja projektu jest wysoce
zaawansowana. W jakiej obecnie roli sprawdziłbyś się najlepiej, jeżeli wziąć pod uwagę
Twoje doświadczenia nabyte przy implementowaniu interfejsów użytkownika?
3.13. Załóżmy, że programiści tworzą system na platformie uniksowej, a dokumentaliści
tworzą dokumenty na macintoshu. Klient wymaga dokumentacji czytelnej również
w systemie Windows. Programiści tworzą dokumentację projektową przy użyciu
Adobe FrameMaker. Zespół dokumentacyjny wykorzystuje MS Word do pisania do-
kumentacji użytkowej. Klient przysyła poprawki, lecz nie żąda ich uwzględnienia
w dokumentach już dostarczonych. Jak należy zorganizować przepływ informacji
(dotyczący narzędzi, formatów i tym podobnych) między programistami, doku-
mentalistami i klientem, by zminimalizować duplikowanie plików i jednocześnie
respektować wybór preferowanej platformy przez każdego z uczestników?
3.14. Jakie zaproponowałbyś zmiany w organizacji i infrastrukturze komunikacyjnej dla
projektu będącego sukcesorem Ariane 5 w obliczu katastrofy rakiety Ariane 501, opi-
sanej na początku tego rozdziału?

Bibliografia
[Barone i Switzer, 1995] J. T. T. Barone, J. Switzer Interviewing: Art and Skill, Allyn & Bacon, 1995.

[Borghoff i Schlichter, 2000] U. W. Borghoff, J. Schlichter Computer Supported Cooperative Work:


An Introduction into Distributed Applications, Springer-Verlag. 2000.

[BSCW] Basic Support for Cooperative Work, http://bscw.gmd.de.

[Curtis i in. 1988] B. Curtis, H. Krasner, N. Iscoe A field study of the software design
process for large systems, „Communications of the ACM", t. 31, nr 11,
1268 - 87, 1988.

[Doyle i Straus, 1982] M. Doyle, D. Straus How to make meetings work, T h e Berkeley
Publishing Group, NY, 1982.
[Fagan, 1976] M. E. Fagan Design and code inspections to reduce errors in program
development, „IBM System Journal", t. 15, nr 3, str. 182 - 211, 1976.

[Fisher i in., 1991] R. Fisher, W. Ury, B. Patton Getting to Yes: Negotiating Agreement
Without Giving In, wyd. drugie, Penguin Books, 1991.
154 Rozdziat 3. • Organizacja projektu i komunikacja

[Gantt, 1910] H. L. Gantt, Work, wages, and profits, „The Engineering Magazine",
New York, 1910.

[Grudin, 1988] J. Grudin „Why CSCW applications fail: Problems in design and evaluation
of organization interfaces", Proceedings CSCW'88, Portland, OR, 1988.

[Kayser, 1990] T. A. Kayser Mining Group Gold, Serif, El Segundo, CA, 1990.

[Kraut i Streeter, 1995] R. E. Kraut, L. A. Streeter Coordination in software development,


„Communications of the ACM", t. 38, nr 3, marzec 1995.

[Lions, 1996] J.-L. Lions ARIANE 5 Flight 501 Failure: Report by the Inquiry Board,
http://www.esrin.esa.it/htdocs/tidc/Press/Press96/ariane5rep.htrnl, 1996.

[Lotus] Lotus http://www.lotus.corn/.

[Microsoft] Microsoft http://www.microsoft.com/.

[OWL, 1996] OWL Project Documentation, School of Computer Science, Carnegie


Mellon University, Pittsburgh, PA, 1996.

[Wiki] Wiki, http:llc2.com/cgilwikKWikiWikiWeh.

[Yahoo] Yahoo, http://groups.yahoo.com/.


4.1. Wstęp: przykłady problemów z użytecznością 158

4.2. O zbieraniu wymagań ogólnie 159

4.3. Koncepcje zbierania wymagań 161


4.3.1. Wymagania funkcyjne 161
4.3.2. Wymagania pozafunkcyjne 162
4.3.3. Kompletność, spójność, jednoznaczność i poprawność 164
4.3.4. Realizm, weryfikowalność i identyfikowalność 165
4.3.5. Inżynieria pierwotna, inżynieria wtórna
i inżynieria interfejsu 165
4.4. Aktywności związane ze zbieraniem wymagań 166
4.4.1. Identyfikacja aktorów 167
4.4.2. Identyfikacja scenariuszy 169
4.4.3. Identyfikacja przypadków użycia 171
4.4.4. Doskonalenie przypadków użycia 173
4.4.5. Identyfikacja relacji między aktorami a przypadkami użycia 176
4.4.6. Początkowa identyfikacja obiektów modelu analitycznego 179
4.4.7. Identyfikacja wymagań pozafunkcyjnych 182

4.5. Zarządzanie zbieraniem wymagań 183


4.5.1. Negocjowanie specyfikacji z klientem:
metoda Joint Application Design 185
4.5.2. Zarządzanie identyfikowalnością 187
4.5.3. Dokumentowanie zbierania wymagań 188

4.6. Analiza przypadku — system ARENA 190


4.6.1. Wstępna deklaracja problemu 190
4.6.2. Identyfikacja aktorów i scenariuszy 192
4.6.3. Identyfikacja przypadków użycia 195
4.6.4. Doskonalenie przypadków użycia i identyfikacja relacji 198
4.6.5. Identyfikacja wymagań pozafunkcyjnych 204
4.6.6. Wnioski 204

4.7. Literatura uzupełniająca 205

4.8. Ćwiczenia 207

Bibliografia 208
4
Zbieranie wymagań

Powszechnym błędem popełnianym przy projektowaniu rzeczy


idiotoodpomych jest niedocenianie geniuszu kompletnych
idiotów.
— Douglas Adams, Mostly Harmless

^ P o d pojęciem wymagania rozumiemy konkretną cechę lub ograniczenie, jakimi musi


charakteryzować się system, by mógł być zaakceptowany przez klienta. Definiowanie wyma-
gań pod adresem tworzonego systemu jest przedmiotem inżynierii wymagań. Inżynieria ta
obejmuje dwie główne aktywności: zbieranie wymagań, uwieńczone specyfikacją systemu zro-
zumiałą dla klienta, oraz ich analizę, prowadzącą do stworzenia modelu, jednoznacznie inter-
pretowalnego przez programistów. Pierwsza z wymienionych aktywności wydaje się być więk-
szym wyzwaniem, wymaga bowiem współpracy wielu grup uczestników o zróżnicowanych
kwalifikacjach: z jednej strony, klienta i użytkowników, wyspecjalizowanych w zakresie dzie-
dziny aplikacyjnej, lecz być może mających nikłe pojęcie o programowaniu, z drugiej nato-
miast, programistów znakomicie znających swój warsztat, lecz być może kompletnie niezo-
rientowanych w realiach codziennej pracy użytkowników powstającego systemu.
Zasypywaniu tej przepaści mają służyć rozmaite scenariusze i przypadki użycia. Scena-
riusz opisuje konkretny przykład wykorzystania systemu jako serię jego interakcji z użytkow-
nikiem; przypadek użycia jest natomiast abstrakcją reprezentującą określoną klasę scenariuszy.
Zarówno scenariusze, jak i przypadki użycia zapisywane są w języku potocznym (naturalnym),
czyli w formie zrozumiałej przez użytkowników.
W tym rozdziale zajmować się będziemy zbieraniem wymagań w oparciu o scenariusze.
Programiści dokonują zbierania wymagań w drodze obserwacji działań użytkowników i uzu-
pełniają tę wiedzę za pomocą wywiadów: naturalistyczne początkowo scenariusze poddawane
są pewnym uogólnieniom, odzwierciedlającym pożądane aspekty funkcjonalności powstają-
cego systemu. Klient i użytkownicy dokonują weryfikacji wizji systemu przez analizę uogól-
nionych scenariuszy, a także na podstawie testowania niewielkich prototypów systemu do-
starczanych przez programistów. W miarę jak definicja systemu stabilizuje się i dojrzewa,
programiści i klient dokonują formalnego aktu porozumienia w kwestii wymagań dla systemu,
w postaci specyfikacji wymagań obejmującej wymagania funkcyjne i pozafunkcyjne, przypadki
użycia oraz scenariusze.
158 Rozdział 4. • Zbieranie wymagań

4.1. Wstęp: przykłady problemów z użytecznością


Mile czy stopy?1
W pewnym eksperymencie geofizycznym wiązka promienia laserowego miała odbić się od zwier-
ciadła promu kosmicznego tak, by trafić w wierzchołek góry. Personel obsługujący eksperyment
wprowadził do systemu wysokość góry jako 10 023, przyjmując, że obowiązującymi w systemie jed-
nostkami są stopy. Jednostkami tymi były jednak mile i wiązka laserowa, zamiast w wierzchołek góry,
trafiła w nieboskłon na (prawie 6000 razy większą) wysokość 10 023 mil.

Separator dziesiętny a separator tysięcy


W USA część dziesiętna liczby oddzielana jest kropką od części całkowitej, natomiast trzycyfrowe
grupy części całkowitej rozdzielane są przecinkami. W Polsce jest dokładnie na odwrót — część dzie-
siętna oddzielana jest za pomocą przecinka, zaś kropka pełni rolę separatora tysięcy. Załóżmy teraz,
iż amerykański oferent zamierza sporządzić ofertę dla polskiego klienta, z cenami wyrażonymi
w dolarach: której z opisanych konwencji powinien użyć, by uniknąć nieporozumień?

Standardowe wzorce
W edytorze tekstu Emacs sekwencja klawiszy Cłrl+X Ctrl+C powoduje wyjście z programu; jeśli
jednak w edytowanym pliku istnieją niezapisane jeszcze zmiany, wyświetlane jest pytanie Save file ...?
(y om) („Czy zapisać plik...?"). Naciśnięcie klawisza y spowoduje zapisanie zmian i wyjście z edytora,
naciśnięcie klawisza n spowoduje wyjście z edytora bez zapisywania zmian. Wiele edytorów hołduje
jednak odmiennej konwencji, wyświetlając w takiej sytuacji zapytanie w rodzaju: „Czy naprawdę
chcesz wyjść z programu?", przy zachowaniu znaczenia klawiszy^ i n jako (odpowiednio) akceptacji
i sprzeciwu. Dotychczasowy użytkownik Emacsa, przesiadając się na edytor tej drugiej kategorii,
może się wiele razy pomylić (z różnym skutkiem), zanim przyzwyczai się do nowej konwencji.

Zbieranie wymagań opiera się na komunikacji między programistami, klientem i użyt-


kownikami, w celu wypracowania spójnej definicji nowego systemu. Załamanie tej komunikacji
i niedostateczne zrozumienie czyichś koncepcji prowadzi nieuchronnie w stronę systemu, który
w najlepszym razie będzie niewygodny w użytkowaniu, a prawdopodobnie nie będzie w ogóle
użyteczny. Błędy popełnione na etapie zbierania wymagań są bardzo kosztowne w eliminowaniu,
zwykle bowiem dają znać o sobie bardzo późno, nawet już po dostarczeniu systemu klientowi.
Błędy te dotyczyć mogą zarówno braków w funkcjonalności systemu, opacznego zrozumienia
wymagań funkcjonalnych, jak i interfejsu użytkownika, który dla osoby pracującej z nim okaże
się mylący lub wręcz bezużyteczny. Z tego względu metody zbierania wymagań zmierzają do
usprawnienia wspomnianej komunikacji. Programiści, obserwując środowisko pracy użytkow-
ników, konstruują model dziedziny aplikacyjnej, po czym formułują jego reprezentację w po-
staci scenariuszy i przypadków użycia, czyli w formie zrozumiałej dla klienta i użytkowników.
Dodatkowo użytkownicy otrzymują proste prototypy swego interfejsu: potencjalny użytkownik
ma do dyspozycji pełną gamę opcji menu, przycisków i innych, jakie mają pojawić się w przy-
szłym systemie. Choć ich klikanie nie daje na razie żadnego efektu — wszak nie zaimplemen-
towano jeszcze ani kawałka funkcjonalności — ich zestaw, układ, hierarchia i tym podobne
stają się podstawą do formułowania pierwszych ocen i spostrzeżeń użytkowników.
Zbieranie wymagań w ogólnym ujęciu, relatywnym do innych aktywności, opiszemy
w sekcji 4.2. W sekcji 4.3 zdefiniujemy podstawowe pojęcia używane w tym rozdziale. W sekcji

' Przykłady zaczerpnięto z książek J. Nielsena [Nielsen, 1993] i P. G. N e u m a n n a [Neumann, 1995],


4.2. O zbieraniu wymagań ogólnie 159

4.4 zajmiemy się aktywnościami wchodzącymi w skład zbierania wymagań, zaś sekcję 4.5
poświęcimy aktywnościom menedżerskim związanym ze zbieraniem wymagań. Przedmiotem
sekcji 4.6 będzie analiza przypadku, jakim jest system ARENA.

4.2. O zbieraniu wymagań ogólnie


Zbieranie wymagań koncentruje się na opisywaniu przeznaczenia systemu. Klient, programi-
ści i użytkownicy identyfikują istniejące problemy i definiują system, którego celem jest roz-
wiązywanie tych problemów. Konkretnym wyrazem tego definiowania jest specyfikacja wy-
magań, stanowiąca kontrakt między klientem a programistami. Strukturalizacja i formalizacja
tej specyfikacji na etapie analizy (którą zajmiemy się w rozdziale 5. „Analiza wymagań") pro-
wadzi do stworzenia modelu analitycznego (którego przykład widzimy na rysunku 4.1). Mo-
del analityczny reprezentuje dokładnie tę samą informację, co specyfikacja wymagań, ma jed-
nak inną postać, wynikającą z odmiennego języka i użycia odmiennej notacji: specyfikacja
wymagań spisana jest w języku naturalnym, podczas gdy model analityczny wyrażony jest
w notacji formalnej (lub półformalnej). Różnica ta wynika z przeznaczenia obu produktów:
specyfikacja wymagań jest płaszczyzną komunikacji między programistami z jednej strony
a klientem i użytkownikami z drugiej; model analityczny stanowi natomiast wspólny punkt
odniesienia dla programistów. Oba produkty są jednak modelem systemu w tym sensie, że
stanowią próbę dokładnego reprezentowania jego zewnętrznych aspektów; w obliczu obu tych
modeli zbieranie wymagań i ich analiza odbywają się równolegle i iteracyjnie.

Rysunek 4.1. Produkty zbierania wymagań i ich analizy

Zbieranie wymagań i ich analiza koncentrują się jedynie na spojrzeniu na system z per-
spektywy jego użytkownika. Częścią wymagań są więc takie aspekty systemu jak jego elementy
funkcjonalne, jego interakcja z użytkownikami, kategoria błędów, na które działanie systemu
powinno być odporne, czy uwarunkowania środowiska, w którym system będzie funkcjono-
wał. Inne jego aspekty, niewidoczne bezpośrednio dla użytkownika — struktura, technologie
implementacyjne, projekt systemu, projekt obiektów — nie są przedmiotem zainteresowania
w fazie zbierania i analizy wymagań.
160 Rozdział 4. • Zbieranie wymagań

Zbieranie wymagań obejmuje następujące aktywności:

• Identyfikowanie aktorów, obejmuje determinowanie przez programistów różnych


typów przyszłych użytkowników systemu.
• Identyfikowanie scenariuszy, odzwierciedlających typowe elementy funkcjonalności
oferowanej przez przyszły system. Programiści obserwują działania potencjalnych
użytkowników i na tej podstawie formułują scenariusze, które służą im zarówno do
komunikowania się ze wspomnianymi użytkownikami, jak i przyczyniają się do coraz
lepszego rozumienia dziedziny aplikacyjnej.
• Identyfikowanie przypadków użycia — gdy tylko programiści i użytkownicy uzgodnią
pewien zbiór realistycznych scenariuszy, scenariusze te są przez programistów gene-
ralizowane w postaci przypadków użycia reprezentujących w sposób kompletny
funkcjonalność przyszłego systemu. Podczas gdy scenariusz jest konkretnym przy-
kładem korzystania z systemu, przypadek użycia jest abstrakcją opisującą wszelkie
możliwe przykłady tego typu. Dysponując kompletem przypadków użycia, programiści
określają zakres funkcjonalny systemu.
• Doskonalenie przypadków użycia — ma na celu dopracowywanie specyfikacji wymagali
pod względem szczegółowości przypadków użycia, jak też identyfikowanie wszelkich
możliwych sytuacji wyjątkowych, jakie mogą się zdarzyć w związku z działaniem systemu.
• Identyfikowanie relacji między przypadkami użycia, wiążącymi ich model w jedną
całość dzięki wyeksponowaniu wspólnej funkcjonalności. Daje to gwarancję, że
specyfikacja wymagań jest spójna.
• Identyfikowanie wymagań pozafunkcyjnych, czyli istotnych dla użytkownika, lecz
niemanifestujących się bezpośrednio w warstwie funkcjonalnej systemu. Należą
do nich między innymi wymagania dotyczące wydajności systemu, jego doku-
mentacji, zapotrzebowania na zasoby, jakości i bezpieczeństwa.

Podczas zbierania wymagań programiści wykorzystują wiele różnych źródeł informacji,


między innymi dostarczane przez klienta dokumenty odzwierciedlające fragmenty dziedziny
aplikacyjnej, dokumentację techniczną i użytkową systemów, które będą zastąpione przez
nowy system oraz — co najważniejsze — informacje na bieżąco przekazywane przez klienta
1 użytkowników, z którymi współpraca w fazie zbierania wymagań i ich analizy jest bardziej
intensywna niż w dalszych stadiach projektu.
W tym rozdziale skupimy się na dwóch metodach zbierania informacji, podejmowa-
nia decyzji wspólnie z klientem i użytkownikami oraz zarządzania zależnościami, jakie wy-
stępują między wymaganiami a innymi artefaktami. Oto te metody.

• Joint Application Design (JAD) („połączony projekt2 aplikacji") — istotą tej metody
jest budowanie konsensusu między programistami, klientem i użytkownikami w celu
wspólnego wypracowania specyfikacji wymagań.
• Identyfikowałność — metoda ta sprowadza się do rejestrowania, strukturalizowania,
łączenia i grupowania zarówno zależności między samymi wymaganiami, jak i ich
relacji do innych produktów projektu.

2
Słowo „projekt" (design) w wyjaśnieniu akronimu JAD może być tu cokolwiek mylące, występuje
bowiem w zupełnie innym znaczeniu niż to, w jakim używane jest w rozdziałach poświęconych
projektowi systemu i projektowi obiektów.
4.3. Koncepcje zbierania wymagań 161

4.3. Koncepcje zbierania wymagań


W sekcji 4.3 opiszemy podstawowe pojęcia i koncepcje zbierania wymagań, wykorzystywane
w tym rozdziale, między innymi:

• Wymagania funkcyjne (patrz sekcja 4.3.1),


• Wymagania pozafunkcyjne (patrz sekcja 4.3.2),
• Kompletność, spójność, jednoznaczność i poprawność (patrz sekcja 4.3.3),
• Realizm, weryfikowalność i identyfikowalność (patrz sekcja 4.3.4),
• Inżynierię pierwotną, inżynierię wtórną i inżynierię interfejsu (patrz sekcja 4.3.5).

Aktywnościami związanymi ze zbieraniem wymagań zajmiemy się w sekcji 4.4.

4.3.1. Wymagania funkcyjne


Wymagania funkcyjne odzwierciedlają interakcje między systemem a jego środowiskiem,
w oderwaniu od implementacji tegoż systemu. Pod pojęciem „środowiska" rozumiemy tu za-
równo użytkowników wspomnianego systemu, jak i inne systemy wchodzące w (potencjalną)
interakcję z przedmiotowym systemem. Poniżej przedstawiono przykład wymagań funkcyjnych
dotyczących inteligentnego („satelitarnego") zegarka SatWatch, którego funkcjonowanie (a szcze-
gólnie — dostosowywanie do bieżącej strefy czasowej) obywa się bez interwencji użytkownika.

SatWatch jest naręcznym zegarkiem wyświetlającym aktualny czas, zgodnie ze strefą czasową
właściwą dla bieżącej lokalizacji. W celu określenia tej lokalizacji (i uzyskania związanych z nią
struktur danych) SatWatch kontaktuje się z satelitami GPS.

SatWatch gwarantuje dokładny pomiar czasu bez potrzeby jakichkolwiek zabiegów dostosowu-
jących ze strony użytkownika. Gdy użytkownik przekracza geograficzną lub polityczną strefę
czasową, następuje automatyczne uwzględnienie tego faktu, w związku z czym SatWatch nie po-
siada żadnych przycisków regulacyjnych dostępnych dla użytkownika. ]ako że działanie zegarka
SatWatch uzależnione jest od systemu GPS, obarczone jest tymi samymi mankamentami i ogra-
niczeniami, co generalnie wszystkie urządzenia korzystające z tego systemu, między innymi niemoż-
nością określenia lokalizacji w pewnych porach dnia w rejonie górzystym. W czasie, gdy GPS
nie jest dostępny, SatWatch przyjmuje, że nie została przekroczona strefa czasowa; gdy tylko
uda m u się nawiązać łączność z GPS, wskazanie czasu jest korygowane do właściwej wartości.

Wyświetlacz zegarka SatWatch ma strukturę dwuwierszową: w pierwszym wierszu wyświetlany


jest czas (godziny, minuty, sekundy i informacja o bieżącej strefie czasowej), w drugim data (dzień
tygodnia, dzień miesiąca, miesiąc i rok). Technologia, w jakiej wykonany jest wyświedacz, gwarantuje
widoczność wyświetlanych danych nawet w niesprzyjających warunkach oświetleniowych.

Na wypadek, gdyby zmieniły się polityczne granice stref czasowych, istnieje możliwość uaktualnienia
programowania zegarka, za pomocą urządzenia WebifyWatch (dostarczanego wraz z zegarkiem)
i komputera podłączonego do internetu.

Zauważmy, że powyższe wymagania koncentrują się jedynie na możliwych interakcjach


zegarka SatWatch ze światem zewnętrznym (właścicielem, systemem GPS i urządzeniem Webi-
fyWatch). Nie ma w nich mowy o jakichkolwiek detalach implementacyjnych (procesorze, języku
programowania czy konkretnej technologii wyświetlacza).
162 Rozdział 4. • Zbieranie wymagań

4.3.2. Wymagania pozafunkcyjne


Wymagania pozafunkcyjne opisują te aspekty systemu, które nie są bezpośrednio związane
z jego funkcjonalnością. Mogą dotyczyć rozmaitych aspektów systemu, od jego użyteczności
do wydajności. Model3 FURPS+ wykorzystywany w ramach jednolitego procesu (Unified Pro-
cess)4, a opisany przez I. Jacobsona, G. Boocha i J. Rumbaugha [Jacobson i in., 1999], wyróżnia
następujące kategorie wymagań pozafunkcyjnych, związane kolejno z:

• Użytecznością rozumianą jako łatwość uczenia się przez użytkownika obsługi systemu
(lub jego komponentu), przygotowywania jego danych wejściowych i interpretowania
wyników. Wymagania tej kategorii mogą dotyczyć konwencji związanych z interfej-
sem użytkownika, zakresu pomocy online, stopnia szczegółowości dokumentacji
i tym podobnych. Często zdarza się, że klient, w celu uproszczenia obsługi systemu,
żąda od programistów zgodności interfejsu GUI z określoną konwencją kolorystyczną,
czcionkami czy firmowym logo.
• Niezawodnością oznaczającą zdolność systemu lub jego komponentu do spełniania
wymaganych funkcji w określonych warunkach i w określonym przedziale czasu.
Elementem niezawodności może być na przykład średni czas pracy między awariami
(MTBF — mean time between failures), wykrywanie specyficznych awarii czy skuteczne
odpieranie ataków na zabezpieczenia. Ostatnio kategorię tę zastąpiła nowa, zwana
pewnością działania (ang. dependability), która oznacza, że usługi świadczone przez
system mogą być uważane za godne zaufania; składają się na nią: niezawodność,
solidność (rozumiana jako zdolność do poprawnego radzenia sobie z nieprawidłowymi
danymi wejściowymi lub uciążliwymi warunkami środowiska, na przykład dużym ob-
ciążeniem) i bezpieczeństwo (będące miarą braku katastroficznych konsekwencji dla
środowiska).
• Wydajnością, czyli mierzalnymi aspektami działania systemu w postaci między innymi
czasu reakcji systemu na akcję użytkownika, przepustowości (mierzonej jako ilość
pracy wykonywanej w jednostce czasu), stopnia dostępności (operatywności) systemu
lub komponentu, gdy zachodzi potrzeba jego użycia oraz dokładności.
• Wspieralności wyrażającej się łatwością wprowadzania zmian do systemu po jego
wdrożeniu, czyli między innymi w związku z pojawianiem się nowych koncepcji
w dziedzinie aplikacyjnej (adaptowalność), usuwaniem usterek, implementowaniem
nowych technologii (łatwość utrzymania) czy poszerzaniem o nowe konwencje re-
gionalne (internacjonalizacją) — języki, jednostki miar, formaty liczb i dat i tym
podobne. Standard ISO 9126 dotyczący jakości oprogramowania [ISO Std. 9126],
podobny do modelu FURPS+, zastępuje tę kategorię dwiema innymi: łatwością
utrzymania i przenośnością rozumianą jako łatwość transferowania systemu lub
jego komponentu między różnymi platformami sprzętowymi i programowymi.

3
FURPS+ jest akronimem utworzonym z pierwszych liter słów Functionality (funkcjonalność), Usability
(użyteczność) Reliability (niezawodność), Performance (wydajność) i Supportability (wspierałność);
znak „+" symbolizuje pozostałe kategorie. Model FURPS został zaproponowany po raz pierwszy w pracy
Grady'ego [Grady, 1992], Jego definicję na potrzeby tego rozdziału zaczerpnęliśmy z opisu standardu
[IEEE Std. 610.12-1990],
4
Patrz sekcja 15.4.2 — przyp. tłum.
4.3. Koncepcje zbierania wymagań 163

Model FURPS+ definiuje dodatkowe kategorie wymagań, generalnie cytowane jako


przykłady wymagań pozafunkcyjnych. Oto one.

• Wymagania implementacyjne dotyczące użycia przy implementowaniu systemu


określonych narzędzi, języków programowania, platform sprzętowych.
• Wymagania dotyczące interfejsu narzucone przez istniejące systemy zewnętrzne
lub standardy formatów wymiany danych.
• Wymagania operacyjne związane z administrowaniem i zarządzaniem ustawie-
niami systemu.
• Wymagania pakietowe określające formę (nośnik) dostarczenia i instalowania systemu.
• Wymagania prawne dotyczące licencjonowania, certyfikatów i generalnie zgodności
z obowiązującymi przepisami. Przykładem wymagań tej kategorii może być wymóg
zgodności oprogramowania opracowywanego na potrzeby rządu USA z sekcją 508
ustawy Rehabilitation Act z 1973 roku, zobowiązującej instytucje publiczne do za-
pewnienia dostępności ich serwisów informacyjnych i usług elektronicznych, ze
szczególnym uwzględnieniem potrzeb osób niepełnosprawnych.

Wymagania pozafunkcyjne, plasujące się w kategoriach URPS, określane są wspólnym


mianem wymagań jakościowych. Wymagania związane z implementacją, eksploatacją, pa-
kietowaniem i uwarunkowaniami prawnymi nazywane są ograniczeniami lub pseudowyma-
ganiami. Wymagania dotyczące budżetu i harmonogramu nie są zwykle traktowane jak wy-
magania pozafunkcyjne, odnoszą się bowiem do wewnętrznych szczegółów projektu (patrz
rozdział 14. „Zarządzanie projektem"). Poniżej widoczne są wymagania pozafunkcyjne dla ze-
garka SatWatch.

Wymagania jakościowe dla zegarka SatWatch


® Każdy, kto potrafi odczytywać czas i datę z zegarka cyfrowego oraz interpretować międzynarodowe
skróty stref czasowych, powinien poradzić sobie z obsługą zegarka SatWatch bez dodatkowej
instrukcji (wymaganie dotyczące użyteczności).
• Jako że SatWatch nie ma zewnętrznych przycisków, nie powinny występować błędy oprogramowania
wymagające konieczności jego resetowania (wymaganie dotyczące niezawodności).
• W przypadku przerwania łączności z systemem GPS SatWatch powinien powrócić do
poprawnego wyświetlania czasu najdalej po upływie 5 minut od wznowienia tej łączności
(wymaganie dotyczące wydajności).
® SatWatch powinien odmierzać czas z dokładnością do 1/100 sekundy przez okres co najmniej
5 lat (wymaganie dotyczące wydajności).
® SatWatch powinien poprawnie wyświetlać czas we wszystkich 24 strefach czasowych (wymaganie
dotyczące wydajności).
« SatWatch powinien mieć możliwość aktualizowania danych wewnętrznych za pomocą interfejsu
USB urządzenia WebifyWatch (wymaganie dotyczące wspieralności).

Ograniczenia dla zegarka SatWatch


• Całe oprogramowanie związane z zegarkiem SatWatch powinno być napisane w języku Java,
zgodnie z aktualną polityką firmy (wymaganie implementacyjne).
• SatWatch powinien być w pełni zgodny z fizycznym, elektrycznym i programowym interfejsem
definiowanym przez WebifyWatch API 2.0 (wymaganie dotyczące interfejsu).
164 Rozdział 4. • Zbieranie wymagań

4.3.3. Kompletność, spójność, jednoznaczność i poprawność


Wymagania nie są kwestią jednorazowego sformułowania, są nieustannie modyfikowane,
a ich rozumienie przez programistów weryfikowane jest przez klienta i użytkowników. Wery-
fikacja ta ma na celu zapewnienie, że zarówno klient, jak i programiści działają zgodnie ze
specyfikacją wymagań. Przedmiotem tej weryfikacji jest sprawdzanie, czy wspomniana specyfi-
kacja jest kompletna, spójna, jednoznaczna i poprawna.
Specyfikacja jest kompletna, jeśli uwzględnia wszystkie możliwe scenariusze, włącznie
z sytuacjami wyjątkowymi — czyli gdy w modelu wymagań uwzględnione są wszystkie aspekty
systemu. Specyfikacja jest spójna, jeżeli nie zawiera wewnętrznych sprzeczności. Jednoznacz-
ność specyfikacji oznacza, że definiuje ona dokładnie jeden system — czyli że nie jest możliwe
jej interpretowanie na kilka różnych sposobów. Wreszcie, specyfikacja jest poprawna, jeśli
odzwierciedla dokładnie ten system, którego potrzebuje klient i który programiści zamierzają
tworzyć (czyli model wymagań dokładnie reprezentuje wszystkie aspekty systemu w sposób
satysfakcjonujący i klienta, i programistów). Znaczenie wymienionych cech zilustrowaliśmy
praktycznymi przykładami w tabeli 4.1.

Tabela 4.1. Weryfikowalne cechy specyfikacji

Cecha Przykład niezgodności Rozwiązanie


Kompletność Specyfikacja zegarka SatWatch nie Dodanie kolejnego wymagania
— wszystkie istotne cechy określa jego zachowania w funkcyjnego, zgodnie z którym
muszą być opisywane sytuacji, gdy przez dłuższy czas synchronizacja zegarka SatWatch
przez wymagania. znajduje się na pograniczu dwóch z bieżącą strefą czasową odbywa
stref czasowych. się nie częściej niż co 5 minut.

Spójność Wymaganiu, by oprogramowanie Zrewidowanie jednego


— żadne dwa wymagania zegarka nie zawierało żadnych z powodujących konflikt
nie mogą przeczyć sobie defektów, nie towarzyszy wymaganie wymagań m o d e l u (na przykład
nawzajem. związanie z możliwością inne s f o r m u ł o w a n i e wymagania
aktualizowania oprogramowania. dotyczącego braku jakichkolwiek
usterek w oprogramowaniu, gdyż
w obecnej postaci wymaganie
to jest i tak nieweryfikowalne).

Jednoznaczność Specyfikacja zegarka SatWatch Sprecyzowanie wieloznacznego


— żadne wymaganie nie określa zasady synchronizacji jego w y m a g a n i a przez decyzję,
może być interpretowane wskazań z bieżącą strefą czasową. czy SatWatch ma automatycznie
na dwa sprzeczne sposoby. Czy SatWatch powinien także uwzględniać czas letni, czy też nie.
uwzględniać automatycznie czas
letni?
Poprawność Wymaganie, by SatWatch wyświetlał Usunięcie ze specyfikacji
— wymaganie opisuje poprawnie czas w każdej z 24 stref, błędnego założenia, iż na kuli
cechy systemu bazuje na błędnym założeniu, że ziemskiej obowiązują 24 strefy
i środowiska istotne dla stref czasowych jest właśnie tyle. czasowe w podziale godzinnym.
klienta i programistów, Jest to nieprawda, gdyż jest ich trochę
nie włączając żadnych więcej: na niektórych obszarach
cech niepożądanych. (między innymi w Indiach) strefy
czasowe wyznaczane są w podziale
półgodzinnym, nie godzinnym.
4.3. Koncepcje zbierania wymagań 165

Poprawność i kompletność to dwie cechy, którym najłatwiej uchybić przy tworzeniu


specyfikacji, szczególnie wtedy, gdy opisuje ona nieistniejący jeszcze system. Z tego względu
specyfikacja, stanowiąca kontrakt między klientem a programistami, musi być szczególnie
uważnie kontrolowana przez obie strony. Dodatkowo te części systemu, które stwarzają szcze-
gólne ryzyko w tym względzie, powinny być odzwierciedlone w formie prototypów lub sy-
mulacji, by użytkownicy mogli sobie wyrobić lepsze wyobrażenie na ich temat. W konkret-
nym przypadku zegarka SatWatch może to być na przykład jego makieta skonstruowana na
bazie tradycyjnego zegarka — przy okazji użytkownik może na przykład uświadomić sobie, że
potrzebuje wyświetlania daty w obu formatach (do wyboru): europejskim i amerykańskim.

4.3.4. Realizm, weryfikowalność i identyfikowalność


Trzema innymi pożądanymi cechami specyfikacji wymagań są: realizm, weryfikowalność
i identyfikowalność. Specyfikacja jest realistyczna, jeśli istnieje fizyczna możliwość zaim-
plementowania opisywanego przez nią systemu, przy uwzględnieniu wszystkich wyartykuło-
wanych ograniczeń. Weryfikowalność specyfikacji to możliwość zaprojektowania testów, które
konfrontować będą zawarte w niej wymagania z faktycznymi własnościami zaimplemento-
wanego systemu. I tak na przykład trudno zweryfikować wymaganie (średnio) stuletniego
okresu bezawaryjnej pracy zegarka SatWatch (nawet przy założeniu, iż samo wymaganie jest
realistyczne samo w sobie). Oto inne przykłady nieweryfikowalnych wymagań.

• Produkt musi posiadać dobry interfejs użytkownika — znaczenie słowa „dobry" nie
jest precyzyjnie zdefiniowane.
• Produkt musi być wolny od wszelkich defektów — zweryfikowanie tego wymagania
wymagałoby użycia nadmiernej ilości zasobów.
• W większości przypadków produkt musi reagować na akcję użytkownika w ciągu
1 sekundy — co to znaczy „w większości przypadków"?

Specyfikacja jest identyfikowalna, jeśli każde z wymagań może być wyraźnie odniesione
do określonego podzbioru funkcji systemu, i vice versa — gdy każda funkcja systemu daje się
zidentyfikować w aspekcie zbioru wymagań przemawiających za jej istnieniem. Identyfiko-
walność obejmuje także możliwość śledzenia zależności między poszczególnymi wymaganiami,
funkcjami systemu i pośrednimi artefaktami projektowymi — komponentami systemu, kla-
sami, metodami i atrybutami obiektów. Staje się ona cechą krytyczną w przypadku planowa-
nia testów i oceniania konsekwencji zmian: dzięki niej tester może określać stopień pokrycia
testowego kodu, czyli identyfikować te wymagania, które faktycznie podlegają przetestowaniu,
zaś programiści są w stanie określać wpływ dokonywanych zmian na poszczególne kompo-
nenty systemu.

4.3.5. Inżynieria pierwotna, inżynieria wtórna i inżynieria interfejsu


Aktywności wchodzące w skład zbierania wymagań mogą być sklasyfikowane w trzech na-
stępujących kategoriach. Zależnie od relacji tworzonego systemu do rzeczywistości, wysił-
ki zmierzające do jego zbudowania można zaklasyfikować bądź jako inżynierię pierwotną
(w języku angielskim nazywaną obrazowo „dziewiczą" — greenfield engineering), polegającą
166 Rozdział 4. • Zbieranie wymagań

na tworzeniu systemu „od zera", gdyż nie istnieje żaden jego pierwowzór, oraz inżynierię
wtórną (nazywaną często niezbyt zgrabnie „reinżynierią", od ang. reingeenering), której istotą
jest ponowne zaimplementowanie lub przeprojektowanie istniejącego systemu, w związku
z nowymi potrzebami biznesowymi lub pojawieniem się nowych technologii. Piszą o tym
M. Hammer i J. Champty [Hammer i Champy, 1993]. Opisywany wcześniej projekt zegarka
SatWatch jest przykładem inżynierii pierwotnej.
Przedmiotem inżynierii interfejsu jest interfejs użytkownika istniejącego systemu
— poza ponownym zaprojektowaniem lub ponownym zaimplementowaniem tego interfejsu,
wszystkie inne cechy systemu pozostają bez zmian. Sytuacja taka występuje wówczas, gdy
rachunek ekonomiczny przemawia raczej za użytkowaniem dotychczasowego systemu niż
za tworzeniem zupełnie nowego.
W przypadku dwóch pierwszych kategorii programiści starają się zebrać jak najwięcej
informacji z zakresu dziedziny aplikacyjnej, którą to informację znaleźć mogą między innymi
w podręcznikach systemu, dokumentacji dla nowych pracowników, słownikach, ściągawkach
i innych notatkach użytkowników, no i — oczywiście — w czasie bezpośrednich wywiadów
z klientem i użytkownikami. Jakkolwiek wywiady te są z założenia nieocenionym narzędziem,
nie pomagają jednak w uzyskiwaniu żądanej informacji, gdy zadaje się niewłaściwe pytania;
dlatego też programiści powinni uzyskać najpierw jak najwięcej wiadomości, zanim zastosują
owo podejście bezpośrednie.
Zajmiemy się teraz szczegółowo aktywnościami związanymi ze zbieraniem wymagań.

4.4. Aktywności związane ze zbieraniem wymagań


Aktywności wchodzące w skład procesu zbierania wymagań stanowią odzwierciedlenie dekla-
racji problemu (patrz rozdział 3. „Organizacja projektu i komunikacja">) w specyfikacji
wymagań, w ramach której deklaracja ta reprezentowana jest w postaci zbioru aktorów, scena-
riuszy i przypadków użycia (patrz rozdział 2. „Modelowanie w języku UML"). W kolejnych
sekcjach opiszemy heurystyki i metody zbierania wymagań od użytkowników oraz modelowa-
nie systemu w świetle tych koncepcji, a mianowicie:

• Identyfikację aktorów (patrz sekcja 4.4.1),


• Identyfikację scenariuszy (patrz sekcja 4.4.2),
• Identyfikację przypadków użycia (patrz sekcja 4.4.3),
• Doskonalenie przypadków użycia (patrz sekcja 4.4.4),
• Identyfikację relacji między aktorami a przypadkami użycia (patrz sekcja 4.4.5),
• Identyfikację obiektów modelu analitycznego (patrz sekcja 4.4.6),
• Identyfikację wymagań pozafunkcyjnych (patrz sekcja 4.4.7).

Opisywane tu metody zaadaptowane zostały na podstawie OOSE [Jacobson i in., 1992],


jednolitego procesu wytwarzania oprogramowania (Unified Software Development Process,
[Jacobson i in., 1999]) i projektowania sterowanego odpowiedzialnością (responsibility-driven
design, [Wirfs-Brock i in., 1990]).
4.4. Aktywności związane ze zbieraniem wymagań 167

4.4.1. Identyfikacja aktorów


Aktorzy są reprezentantami zewnętrznych encji, przejawiających interakcje z projektowanym
systemem. Aktorem może być człowiek łub system zewnętrzny. W przykładzie z zegarkiem
SatWatch aktorami są: jego właściciel, satelity GPS i urządzenie WebifyWatch (patrz rysu-
nek 4.2) — wszystkie te encje wymieniają informację z zegarkiem, przy czym każdy z tych
aktów wymiany cechuje się specyficzną interakcją: właściciel zegarka nosi go na przegubie
i odczytuje czas oraz datę, satelity GPS podają zegarkowi niezbędne informacje, zaś urządzenie
WebifyWatch umożliwia mu załadowanie uaktualnionych danych. Aktorzy uosabiają zatem
określone klasy funkcjonalności systemu.

Rysunek 4.2. Aktorzy związani z systemem SatWatch. WatchOwrer wykorzystuje wskazania zegarka,
zegarek współdziała z GPS-em w celu ustalenia bieżącej lokalizacji, zaś urządzenie Webi fyWatch umożliwia
aktualizację wewnętrznych danych związanych z polityką zmiany czasu (na przykład zmianą początkowej
i końcowej daty czasu letniego)

Rozpatrzmy przykład bardziej skomplikowany — cytowany wcześniej system FRIEND,


umożliwiający zarządzanie wypadkami i sytuacjami kryzysowymi na podstawie informacji
rozproszonej ([Bruegge i in., 1994]). W interakcję z tym systemem uwikłanych jest wielu akto-
rów: Fi el dOf f i cer, reprezentujący funkcjonariusza policji lub strażaka reagującego na incydent,
Dispatcher, reprezentujący dyspozytora odpowiedzialnego za przyjmowanie zgłoszeń pod nu-
merem 911 i przydział zasobów do obsługi incydentu. System FRIEND współdziała z tymi
aktorami, realizując śledzenie incydentów, zasobów i planów zadań. System ten zapewnia tak-
że dostęp do informacji zawartych w różnych bazach danych, takich jak ewidencja materiałów
niebezpiecznych czy listy kontrolne procedur operacyjnych. Współdziałanie systemu z aktorami
Fi el dOff i cer i Di spatcher odbywa się w oparciu o różne interfejsy: pierwszy z nich posługuje
się przenośnym komputerem, drugi — stacją roboczą (patrz rysunek 4.3).

Rysunek 4.3. Aktorzy współdziałający z systemem FRIEND. Aktorzy nie tylko wykorzystują różne
elementy funkcjonalności systemu, lecz także posługują się różnymi komputerami
168 Rozdział 4. • Zbieranie wymagań

Aktorów należy utożsamiać raczej z abstrakcyjnymi rolami niż konkretnymi osobami:


ta sama osoba może przecież wcielać się w różnym czasie w role Fi el dOf f i cer i Di spatcher,
lecz przypisane do tych ról funkcje są diametralnie odmienne, więc role te modelowane są
w postaci dwóch różnych aktorów.
Identyfikacja aktorów jest pierwszym krokiem w procesie zbierania wymagań, jest bo-
wiem niezbędna zarówno do wyznaczenia granic tworzonego systemu, jak i do określenia
wszystkich perspektyw, z jakich spoglądać powinni na ten system jego programiści. Gdy sys-
tem tworzony jest dla istniejącej organizacji (przedsiębiorstwa), większość aktorów istnieje już
a priori, jeszcze przed rozpoczęciem prac nad systemem — aktorzy ci uosabiają po prostu role
pełnione w firmie przez poszczególnych pracowników.
W początkowej fazie identyfikowania aktorów zdarza się, że trudno odróżnić aktorów
od obiektów systemu, przykładowo podsystem bazy danych może być w pewnych okoliczno-
ściach uważany za aktora, w pewnych zaś za obiekt systemu. Gdy jednak wyznaczone zostaną
granice systemu, rozróżnienie to jest jednoznaczne: aktorzy zlokalizowani są na zewnętrz tych
granic, zaś podsystemy i obiekty systemu wewnątrz nich. Baza danych, utrzymywana w formie
odrębnego, niezależnego systemu jest więc aktorem, z perspektywy korzystającego z niej przed-
miotowego systemu.
W związku z identyfikowaniem aktorów programiści zadawać mogą różne pytania,
przykładowe zostały wyszczególnione w ramce.

Przykładowe pytania w związku z identyfikowaniem aktorów:


• Jakie grupy użytkowników w swej pracy wykorzystywać będą system?
• Które z tych grup korzystać będą z zasadniczych funkcji systemu?

• Które z tych grup zajmować się będą funkcjami wtórnymi, takimi jak administrowanie i utrzymanie?
• Z jakim zewnętrznym sprzętem lub oprogramowaniem współpracować ma tworzony system?

W opisywanym już systemie FRIEND powyższe pytania prowadzą do powstania listy


potencjalnych aktorów, którymi są między innymi strażak, funkcjonariusz policji, dyspozytor,
śledczy, komendant, gubernator, ewidencja materiałów niebezpiecznych sporządzona przez
EPA\ administrator systemu i tak dalej. Tę kandydacką listę należy jednak skonsolidować
do mniejszych rozmiarów, tak by jej pozycje odzwierciedlały różne punkty spojrzenia na
system ze strony jego użytkowników. I tak na przykład zarówno strażak, jak i oficer policji
w identyczny sposób korzystają z systemu FRIEND, dodatkowo w danej chwili uwikłani są
w jeden, konkretny incydent. Dla odróżnienia — dyspozytor odpowiedzialny jest być może za
zarządzanie kilkoma incydentami w danej chwili, wymaga więc dostępu do większej ilościowo
i jakościowo informacji. Z kolei komendant czy gubernator niekoniecznie muszą współdziałać
z systemem bezpośrednio, korzystając w zamian z usług wprawnego operatora.
Gdy tylko dokonana zostanie identyfikacja aktorów, kolejnym krokiem w procesie zbie-
rania wymagań jest określenie zakresu funkcjonalności dostępnego dla każdego z aktorów. Tę
informację uzyskuje się za pośrednictwem scenariuszy oraz sformalizowanych przypadków
użycia.

3
EPA — Environmental Protection Agency, Agencja Ochrony Środowiska, federalna organizacja rządu
USA — przyp, tłum.
4.4. Aktywności związane ze zbieraniem wymagań 169

4.4.2. Identyfikacja scenariuszy


Zgodnie z tym, co w swojej książce napisał J. M. Carrol [Carroll, 1995], scenariusz jest „narra-
cyjnym opisem działań i wrażeń ludzi próbujących korzystać z programu lub systemu kom-
puterowego". Scenariusz jest skoncentrowanym, nieformalnym opisem pojedynczej cechy
systemu z perspektywy konkretnego aktora. Scenariusze nie są zamiennikami przypadków
użycia, ponieważ dotyczą konkretnych zdarzeń spośród całej gamy możliwych zdarzeń danego
typu; zwiększają jednak jakość zbierania wymagań jako narzędzie zrozumiałe dla klienta i użyt-
kowników. W tabeli 4.2 widzimy scenariusz, zgodnie z którym funkcjonariusz policji raportuje
zaistnienie pożaru, a dyspozytor zapoczątkowuje reakcję na to głoszenie. Zauważmy, że opi-
sywane jest konkretne zdarzenie, a nie ogólna sytuacja zgłaszania zauważonego pożaru.

Tabela 4.2. Scenariusz warehouseOnFi re jako instancja przypadku użycia Report Emergency

Nazwa warehouseOnFire

Instancje aktorów bob, alice:FieldOfficer


uczestniczących ,iohn:Di spatcher

Przepływ zdarzeń 1. Bob, patrolując główną ulicę, zauważa przez o k n o s a m o c h o d u dym


wydobywający się z hurtowni. Jego partnerka Alice aktywuje funkcję
„Raport Emergency" na laptopie systemu FRIEND.
2. Alice wprowadza do formularza adres budynku, jego orientacyjną lokalizację
(północno-zachodni zakątek) i rozmiar zagrożenia. Dodatkowo żąda
kilku jednostek pomocy paramedycznej, gdyż okolica pożaru jest dość
gęsto zaludniona. Wysyła formularz i czeka na potwierdzenie.
3. Dyspozytor (Di s p a t c h e r ) John zostaje z a a l a r m o w a n y przez sygnał
dźwiękowy na swojej stacji roboczej. Odczytuje informację od Alice
i wysyła potwierdzenie raportu. Aktywuje jednostkę gaśniczą oraz dwie
jednostki paramedyczne i wysyła do Alice informację o szacunkowym
czasie oczekiwania na przybycie (ETA — Estimated Arrival Time).
4. Alice odczytuje potwierdzenie i informację o ETA.

Scenariusze nie powinny opisywać podejmowania decyzji — do tego potrzebna jest para
scenariuszy: jeden odpowiadający decyzji na „tak", drugi odpowiadający decyzji na „nie".
W procesie zbierania wymagań — a także w innych stadiach projektu — scenariusze
mogą mieć rozmaite zastosowania. W pracy J. M. Carrola [Carroll, 1995] wyróżniono kilka
typowych grup scenariuszy.
• Scenariusz bieżący opisuje aktualną sytuację. Przykładowo w ramach inżynierii
wtórnej programiści obserwują zachowanie użytkowników aktualnie eksploatowane-
go systemu, opisując ich akcje w formie scenariuszy. Scenariusze tej kategorii są póź-
niej weryfikowane przez użytkowników pod kątem poprawności i dokładności.
• Scenariusz wizjonerski opisuje fragment systemu istniejącego dopiero w zamyśle
projektantów. Taki scenariusz może być zarówno punktem w przestrzeni modelowa-
nia programistów, którzy w ten sposób doskonalą swe wyobrażenie na temat przy-
szłego systemu, jak i medium komunikacji z użytkownikami. Scenariusze wizjonerskie
mogą być uważane za tanie odmiany prototypów.
170 Rozdział 4. • Zbieranie wymagań

• Scenariusz ewaluacyjny opisuje zadania użytkowników, pod kątem których oceniany


jest system. Wspólne wypracowywanie takich scenariuszy przez programistów i użyt-
kowników wpływa także na udoskonalenie definicji funkcjonalności testowanej przez
te scenariusze.
• Scenariusz treningowy to przewodnik służący przystosowaniu nowych użytkowni-
ków do pracy z systemem. Krok po kroku instruuje on użytkownika, jak ma wyko-
nywać poszczególne zadania.

Większość scenariuszy początkowo jest niekompletna i ma charakter ogólnikowy, tak jak


przykładowy scenariusz warehouseOnFi re. W identyfikowaniu potencjalnych scenariuszy po-
mocne mogą okazać się następujące pytania.

Przykładowe pytania pomocne w identyfikowaniu scenariuszy


• Spełniania jakich zadań oczekuje aktor od systemu?
• Dostęp do jakiej informacji jest dla aktora niezbędny? Kto generuje tę informację? Czy może być
ona modyfikowana? Jeśli tak, to kiedy i przez kogo?
• O jakich zdarzeniach zewnętrznych system powinien być informowany przez aktora? Jak często?
Kiedy?
• O jakich zdarzeniach aktor powinien być informowany przez system? Z jakim opóźnieniem?

Odpowiedzi na te pytania znajdują programiści w dokumentach związanych z dziedziną


aplikacyjną: w podręczniku użytkownika poprzedniego systemu, podręcznikach procedur,
standardach organizacji, notatkach i ściągawkach użytkowników, a także w drodze wywiadu
z klientem i użytkownikami. Programiści powinni zapisywać scenariusze, używając termino-
logii z zakresu dziedziny aplikacyjnej zamiast swej własnej terminologii. W miarę jak zyskują
oni coraz to lepsze wyobrażenie o dziedzinie aplikacyjnej i możliwościach obecnych technologii,
doskonalą scenariusze w sposób iteracyjny i przyrostowy, wyposażając je w coraz większą ilość
szczegółów. Sporządzenie makiety interfejsu użytkownika często pomaga odnajdywać luki
w specyfikacji i tym samym budować bardziej adekwatny obraz systemu.
W przykładowym systemie FRIEND możemy wyróżnić cztery scenariusze odzwiercie-
dlające typy zadań, jakich wspierania oczekuje się od tego systemu:

• warehouseOnFi re (patrz tabela 4.2): zauważono pożar hurtowni; dwoje funkcjo-


nariuszy przybywa na miejsce i żąda niezbędnych zasobów;
• fenderBender 6 : na autostradzie zdarzył się wypadek samochodowy, na szczęście bez
ofiar, policjanci dokumentują szczegóły wypadku i organizują ruch w jego otoczeniu,
podczas gdy uszkodzone samochody usuwane są na pobocze;
• catlnATree: na wysokie drzewo wdrapał się kot i ma kłopoty z zejściem, wezwano
straż pożarną, zdarzenie nie należało do kategorii pilnych, zatem wóz strażacki przy-
jechał dopiero po dłuższej chwili; ponieważ w międzyczasie niecierpliwy (i pewnie
nieświadomy meandrów systemu FRI END) kot niefortunnie zeskoczył z drzewa, łamiąc
sobie nogę, konieczne jest wezwanie ambulansu weterynaryjnego;

6
Z ang. fender-bender to „stłuczka" — p>'zyp- tłum.
4.4. Aktywności związane ze zbieraniem wymagań 171

• earthQuake: nieprzewidywańe wstrząsy sejsmiczne poważnie uszkodziły budynki


i drogi, wywołując serię incydentów wtórnych, wymagających uruchomienia planu
awaryjnego na szczeblu administracji stanowej, powiadomiony został gubernator,
uszkodzenie dróg utrudnia właściwe reagowanie na incydenty.

W trakcie identyfikowania aktorów i możliwych scenariuszy programiści coraz lepiej


rozumieją dziedzinę aplikacyjną i zyskują wyraźniejszą wizję przyszłego systemu. Gdy sporzą-
dzony zostanie już stabilny zbiór aktorów i scenariuszy, następuje kolejny krok — formalizacja
scenariuszy w postaci przypadków użycia.

4.4.3. Identyfikacja przypadków użycia


Scenariusz jest instancją przypadku użycia — przypadek użycia reprezentuje wszystkie możliwe
scenariusze związane z określonym aspektem funkcjonalności. Przypadek użycia inicjowany jest
przez konkretnego aktora, po czym może oddziaływać także na innych aktorów. Przypadek
użycia reprezentuje kompletny przepływ zdarzeń w systemie w tym sensie, że opisuje serię
powiązanych interakcji wywołanych wspomnianą inicjacją. W tabeli 4.3 pokazany jest przy-
padek użycia ReporEmergency, którego instancją jest scenariusz warehouseOnFi re z tabeli 4.2.
Aktor Fi el dOf f i cer inicjuje ten przypadek użycia, aktywując funkcję „Report Emergency"
systemu FRI END. Przypadek użycia kończy się, gdy aktor Fi el dOf f i cer odbiera potwierdzenie od
dyspozytora. Z opisu poszczególnych kroków przepływu zdarzeń wynika jednoznacznie, kto
inicjuje każdy krok: kroki 1. i 3. inicjowane są przez aktora, kroki 2. i 4. — przez system.
Przypadek użycia ma charakter ogólny i może obejmować cała gamę scenariuszy — przy-
kładowo nie tylko scenariusz warehouseOnFi re, ale także i scenariusz fenderBender może być
uważany za instancję przypadku użycia ReportEmergency.
Podobnie jak scenariusze, tak i przypadki użycia mogą być sporządzane na różnym
stopniu szczegółowości. Wysokopoziomowe przypadki użycia, będące pierwszym efektem
generalizowania scenariuszy, pomagają programistom w definiowaniu zakresu systemu. Pro-
gramiści opatrują przypadki użycia nazwami, przypisują im aktorów inicjujących i sporządzają
wysokopoziomowy opis podobny do tego z tabeli 4.3. Nazwa przypadku użycia powinna mieć
formę czasownika odzwierciedlającego czynność, którą aktor inicjujący usiłuje wykonać: cza-
sownik ReportEmergency („zgłoś niebezpieczeństwo") oznacza po prostu, że aktor Field
"-•Officer przystępuje do zgłoszenia incydentu systemowi FRIEND (i pośrednio aktorowi
Di spatcher). Zauważmy, że przypadek użycia nie został nazwany RecordEmergency („zarejestruj
niebezpieczeństwo"), gdyż wówczas odzwierciedlałby działanie systemu, nie aktora. Równie
nietrafna byłaby w tej roli nazwa Attempt to Report an Emergency („spróbuj wysłać raport o nie-
bezpieczeństwie"), gdyż odzwierciedlałaby konkretną aktywność, a nie cel przypadku użycia.
Przypisanie poszczególnym przypadkom użycia aktorów inicjujących pomaga progra-
mistom lepiej zrozumieć role poszczególnych użytkowników. Często w ramach poszuki-
wania aktorów inicjujących programiści identyfikują istnienie nowych aktorów, o których
uprzednio zapomnieli.
Opis przypadku użycia obejmuje zwykle cztery pola. Opisując warunki wstępne i warunki
końcowe, programiści zdają sobie sprawę z tego, co jest konieczne do uruchomienia danego
przypadku użycia i jakie konsekwencje uruchomienie to wywołuje w środowisku systemu.
Może się zdarzyć, iż analizując warunki wstępne i końcowe poszczególnych przypadków użycia,
programiści identyfikują konieczność definiowania nowych przypadków użycia — i tak na
172 Rozdział 4. • Zbieranie wymagań

Tabela 4.3. Przykładowy przypadek użycia — ReportEmergency. W opisie przepływu zdarzeń lewa
kolumna identyfikuje akcje podejmowane przez aktorów, prawa — reakcje systemu

Nazwa przypadku użycia ReportEmergency

Aktorzy uczestniczący • F i e l d O f f i c e r — inicjuje przypadek użycia


• D i s p a t c h e r — komunikuje się z przypadkiem użycia

Przepływ zdarzeń 1. F i e l d O f f i c e r aktywuje funkcję „Report Emergency" na swym


terminalu.
2. System FRIEND wyświetla w odpowiedzi stosowny
formula ,rz.
3. Fi el dOffi cer vvypelnia formularz, określając typ zdarzenia, stopień
zagrożenia, jeg o lokalizację i krótki opis sytuacji. F i e l d O f f i c e r
określa także m ożliwe sposoby pierwszej reakcji na zagrożenie. Po
wypełnieniu fiarmularza Fi el dOf f i c e r wysyła go do systemu.
4. System FRIEND powiadamia dyspozytora (Di s p a t c h e r )
o zdarzęiniu.
5. Dispatcher an£dizuje informacje zawarte w formularzu, po czym
tworzy nowy obliekt Incident wbazie, realizując przypadek użycia
Openlncident.[ )i spatcher określa sposób reakcji i zatwierdza raport.
6. System FRIEND i n f o r m u j e aktora F i e l d O f f i c e r
o zatwiierdzeniu r a p o r t u i p r z e k a z u j e m u i n f o r m a c j ę
od dysp ozytora.

Warunki wstępne F i e l d O f f i c e r jest zalogowany do systemu FRIEND

Warunki końcowe • F i e l d O f f i c e r otrzymał potwierdzenie przesłania raportu oraz


wiadomość od dyspozytora ( D i s p a t c h e r )
lub
• F i e l d O f f i c e r otrzymał od systemu wyjaśnienie, dlaczego
transakcja nie in o ż e zostać zrealizowana.
Wymagania jakościowe • Raport aktora 17 i el dOffi c e r zostaje potwierdzony w czasie
nie dłuższym n:iż 30 sekund.
• F i e l d O f f i c e r otrzymuje odpowiedź nie później niż 30 sekund
po wysłaniu jej przez dyspozytora.

p r z y k ł a d g d y p r z y p a d e k użycia o b e j m u j e u r u c h o m i e n i e p l a n u a w a r y j n e g o w związku z trzę-


s i e n i e m z i e m i , s p e c y f i k a c j a w y m a g a ń p o w i n n a t a k ż e z a w i e r a ć p r z y p a d e k użycia o d z w i e r -
ciedlający to u r u c h o m i e n i e . Z kolei s p o r z ą d z a n i e opisu przepływu zdarzeń umożliwia p r o g r a -
m i s t o m i k l i e n t o m d y s k u t o w a n i e interakcji m i ę d z y a k t o r a m i a s y s t e m e m , co w rezultacie daje
p o d j ę c i e r o z m a i t y c h decyzji w kwestii g r a n i c m i ę d z y s y s t e m e m a a k t o r a m i , czyli p o d z i a ł u
akcji n a w y k o n y w a n e p r z e z a k t o r ó w i b ę d ą c e o d p o w i e d z i ą s y s t e m u n a p o c z y n a n i a t y c h ż e .
W r e s z c i e , towarzyszące p r z y p a d k o w i użycia wymagania jakościowe d a j ą p r o g r a m i s t o m okazję
d o lepszego p o z n a n i a w y m a g a ń z w i ą z a n y c h z o k r e ś l o n y m a s p e k t e m f u n k c j o n a l n o ś c i . M i m o
iż w p r a k t y c e n i c n i e stoi n a p r z e s z k o d z i e r o z s z e r z a n i u w s p o m n i a n e g o o p i s u o k o l e j n e pola,
z w i ą z a n e m i ę d z y i n n y m i ze z d a r z e n i a m i w y j ą t k o w y m i , r o l a m i czy n i e z m i e n n i k a m i , w książce
tej o g r a n i c z y m y się tylko d o czterech w y m i e n i o n y c h — w a r u n k ó w w s t ę p n y c h , w a r u n k ó w k o ń -
c o w y c h , p r z e p ł y w u z d a r z e ń i w y m a g a ń jakościowych — j a k o określających n a j b a r d z i e j istotne
a s p e k t y p r z y p a d k u użycia.
4.4. Aktywności związane ze zbieraniem wymagań 173

Pisanie przypadków użycia jest umiejętnością, którą analityk doskonali poprzez prak-
tykę. Doskonalenie to wiąże się z wypracowywaniem własnego stylu, co — niestety — prowa-
dzi do zróżnicowania stylów poszczególnych analityków i trudności w tworzeniu spójnych
specyfikacji wymagań. Analitycy mogą uniknąć tego zagrożenia, posiłkując się określonymi
wytycznymi dotyczącymi pisania przypadków użycia. Poniżej widzimy przykład takich wy-
tycznych, zaczerpnięty z pracy A. Cockburna [Cockburn, 2001], zaś w tabeli 4.4 widoczny jest
przykład przypadku użycia wyraźnie odbiegającego od tych wytycznych.

Prosty przewodnik dla autorów przypadków użycia


• Nazwa przypadku użycia powinna być czasownikiem odzwierciedlającym czynność, jaką zamierza
wykonać użytkownik (na przykład ReportEmergency, Openlnci dent).
• Aktorzy powinni być opatrywani nazwami w f o r m i e rzeczowników (Fi el dOf f i c e r ,
Dispatcher, Victim).
• Granice systemu powinny być wyraźnie zaznaczone, poprzez rozróżnienie kroków wykonywanych
przez aktorów i przez system (w tabeli 4.3 poszczególne kroki zostały w tym celu podzielone
między dwie kolumny).
• Opis poszczególnych kroków powinien być formułowany z użyciem formy czynnej czasownika,
co wyraźnie określa aktora odpowiedzialnego za każdy z kroków 7 .
• W opisie kolejnych kroków powinny być widoczne związki przyczynowo-skutkowe.
• Przypadek użycia powinien opisywać kompletną transakcję użytkownika (przypadek użycia
ReportEmergency zawiera opis wszystkich kroków, począwszy od wysłania r a p o r t u , aż
do otrzymania potwierdzenia).
• Sytuacje wyjątkowe powinny być opisywane oddzielnie.
• Przypadek użycia powinien abstrahować od interfejsu użytkownika, który lepiej interpretowany
jest w formie makiety, powinien natomiast skupiać się na czynnościach użytkownika. W przykładzie
z tabeli 4.3 jest mowa o funkcji „Raportuj zdarzenie" w oderwaniu od menu, przycisków czy
poleceń związanych z tą funkcją.
• Rozmiar przypadku użycia nie powinien przekraczać dwóch, trzech stron. Obszerniejsze
przypadki użycia kwalifikują się do dekompozycji za pomocą relacji rozszerzania i zawierania
(którymi zajmiemy się w sekcji 4.4.5).

Przykład z tabeli 4.3 jest wystarczającą ilustracją tego, jak system FRIEND obsługuje
powiadamianie o incydentach i jak współpracują z nim poszczególni użytkownicy: jest on
jednak zbyt mało szczegółowy, by mógł stanowić element specyfikacji wymagań. Zobacz-
my więc, w jaki sposób możliwe jest doskonalenie przypadków użycia i wzbogacanie ich
w niezbędne szczegóły.

4.4.4. Doskonalenie przypadków użycia


W tabeli 4.5 widoczna jest udoskonalona wersja przypadku użycia ReportEmergency. Zawiera
ona dodatkowe szczegóły związane między innymi z rodzajami zdarzeń, jakie rozróżnia
system FRIEND, i sposobem potwierdzania zgłoszenia.

7
Także w języku polskim strona czynna („Policjant wzywa karetkę") udostępnia więcej informacji
niż strona bierna („Wezwana została karetka"), czy użycie trybu zwrotnego („Zjawia się karetka")
— tylko w pierwszym przypadku jest mowa o policjancie — przyp. tłum.
174 Rozdział 4. • Zbieranie wymagań

Tabela 4.4. Przykład nieprawidłowo sporządzonego przypadku użycia (wraz z komentarzem)

Cecha przypadku użycia Opis Co jest źle?


Nazwa przypadku użycia Accident Nieodpowiednia nazwa: jaki jest
cel działań użytkownika?
Aktor inicjujący FieldOfficer

Przepływ zdarzeń 1. F i e l d O f f i c e r raportuje


niebezpieczeństwo.
2. Fi el d O f f i c e r o t r z y m u j e Brak związku przyczynowo-
potwierdzenie przyjęcia skutkowego: jaka i czyja akcja
zgłoszenia. spowodowała, że Fi el dOf f i cer
otrzymał potwierdzenie?
3. Wezwana zostaje karetka Strona bierna: kto wezwał karetkę?
4. Gdy karetka przybywa na Niekompletna transakcja: co robi
miejsce zdarzenia, Di spatcher Di spatcher po wysłaniu karetki
jest informowany o tym fakcie. na miejsce zdarzenia?

Tabela 4.5. Uszczegółowiony przypadek użycia Report Emergency; dodane szczegóły wyróżnione są kursywą

Nazwa przypadku użycia ReportEmergency

Aktorzy uczestniczący • Fi el dOf f i cer — inicjuje przypadek użycia.


• Di s p a t c h e r — komunikuje się z przypadkiem użycia.

Przepływ zdarzeń 1. Fi el dOf f i cer aktywuje funkcję „Raportuj zdarzenie" na swym terminalu.
2. System FRI END wyświetla w odpowiedzi stosowny formularz.
Formularz ten zawiera pola określające między innymi rodzaj
niebezpieczeństwa (ogólne, pożar, komunikacyjne), lokalizację
zdarzenia, jego opis, żądanie zasobów i udział materiałów
niebezpiecznych.
3. Fi el dOf f i c e r wypełnia formularz z obowiązkowymi polami
reprezentującymi typ zdarzenia i jego krótki opis. Fi el dOf f i cer określa
także możliwe sposoby pierwszej reakcji na zagrożenie i wymagane
do tej reakcji zasoby. Po wypełnieniu f o r m u l a r z a F i e l d O f f i c e r
wysyła go do systemu.
4. System FRI END p o w i a d a m i a dyspozytora (Di s p a t c h e r )
o zdarzeniu poprzez wyświetlenie okna ze stosownym
komunikatem i sygnał dźwiękowy.
5. Di spatcher analizuje informacje zawarte w formularzu, po czym tworzy
nowy obiekt Inci dent w bazie, realizując przypadek użycia Openlnci dent.
Cała informacja zawarta we wspomnianym formularzu automatycznie
włączana jest do obiektu Incident. Di spatcher określa sposób reakcji,
dokonując przydziału odpowiednich zasobów (reprezentowanego
przez przypadek użycia Al T o c a t e R e s o u r c e s j i zatwierdza raport,
wysyłając stosowny komunikat do aktora Fi el dOf f i cer.
6. System FRI END informuje aktora Fi el dOf f i cer o zatwierdzeniu
raportu i przekazuje m u informację od dyspozytora.
Warunki wstępne [...]
Warunki końcowe [...]
Wymagania jakościowe
jakościowe [...]
[...]
4.4. Aktywności związane ze zbieraniem wymagań 175

Definiowanie funkcjonalności systemu za pomocą scenariuszy i przypadków użycia ma


na celu przede wszystkim określenie wymagań i ich weryfikację przez klienta we wczesnym
stadium rozwoju systemu. Gdy zaczyna się projektowanie systemu i jego implementowanie,
wtedy modyfikowanie wymagań i dodawanie nowych, nieprzewidzianych wcześniej ele-
mentów funkcjonalnych jest znacznie trudniejsze i bardziej kosztowne. I mimo iż nie spo-
sób uchronić się całkowicie przed perspektywą zmiany wymagań w późniejszych fazach
projektu, to jednak należy się starać, by załatwić wszelkie sprawy związane z wymaganiami
na etapie ich zbierania.
Większość przypadków użycia nie jest tworzona w jednorazowym akcie: niektóre są wiele
razy przepisywane, niektóre kompletnie zarzucane. Aby zaoszczędzić jak najwięcej czasu, wy-
korzystuje się scenariusze i makiety interfejsu użytkownika.
Podczas pisania scenariuszy i przypadków użycia mogą być pomocne następujące
heurystyki.

Heurystyki pisania scenariuszy i przypadków użycia


® Wykorzystuj scenariusze do komunikacji z użytkownikami w celu weryfikowania funkcjonalności
systemu.
• Najpierw dopracuj pojedynczy scenariusz, by zrozumieć założenia użytkownika pod adresem
nowego systemu. Być może użytkownik ma już praktykę w użytkowaniu podobnych systemów,
więc przyjęcie specyficznych konwencji w zakresie interfejsu użytkownika może uczynić system
bardziej użytecznym.
• Następnie zdefiniuj wiele nie za bardzo szczegółowych scenariuszy w celu wypracowania
(w drodze negocjacji z użytkownikiem) zakresu funkcjonalnego systemu.
• Wykorzystuj makiety jedynie jako wizualne narzędzia pomocnicze; projektowanie interfejsu
użytkownika, jako odrębne zadanie, rozpocznie się wówczas, gdy kształt funkcjonalności
systemu stanie się wystarczająco stabilny.
• Prezentuj użytkownikowi wiele różnych wariantów rozwiązań, nie wymagając wyboru
konkretnego. Przyczyni się to do poszerzenia jego horyzontu myślowego w zakresie oczekiwań
pod adresem przyszłego systemu, Ciebie natomiast skłoni do myślenia poza utartymi schematami.
® Gdy dany aspekt funkcjonalny, reprezentowany przez konkretny scenariusz, stanie się dla Ciebie
już wystarczająco zrozumiały, wzbogacaj go o nowe szczegóły, ale pod kontrolą użytkownika.

Celem przedstawionych aktywności jest zapewnienie kompletności i poprawności spe-


cyfikacji wymagań. Programiści identyfikują elementy funkcjonalne, dla których nie opraco-
wano scenariuszy, i dokumentują te elementy, modyfikując istniejące przypadki użycia i defi-
niując nowe. Programiści uwzględniają także sytuacje rzadko występujące i obsługę zdarzeń
wyjątkowych z perspektywy aktorów. Gdy wstępna identyfikacja przypadków użycia i akto-
rów zaowocuje wyznaczeniem granic systemu, przypadki użycia są w dalszym ciągu rozbu-
dowywane o kolejne szczegóły dotyczące cech prezentowanych przez system i jego ograniczeń.
Szczególnie następujące aspekty przypadków użycia, początkowo ignorowane, stają się teraz
przedmiotem szczegółowych definicji:

• elementy podlegające przetwarzaniu przez system — w tabeli 4.5 są to wyróżnio-


ne kursywą szczegóły formularza raportowania i typy incydentów,
176 Rozdział 4. • Zbieranie wymagań

• niskopoziomowe elementy interakcji między systemem a aktorem — w tabeli 4.5 jest


to opis generowania potwierdzenia i dysponowania zasobami przez aktora Di spatcher,
• uprawnienia w zakresie inicjowania poszczególnych przypadków użycia przez po-
szczególnych aktorów,
• sytuacje wyjątkowe i ich obsługiwanie,
• elementy funkcjonalności wspólne dla wielu przypadków użycia.

W następnej sekcji opiszemy sposoby zarządzania relacjami między przypadkami użycia


i aktorami oraz reorganizację przypadków użycia na podstawie tych relacji — przy okazji do-
starczymy przykłady dla trzech ostatnich pozycji powyższej listy.

4.4.5. Identyfikacja relacji między aktorami a przypadkami użycia


Nawet dla średniej wielkości systemu opracowuje się zwykle dużą liczbę przypadków użycia.
Ponieważ są one podporządkowane wspólnemu celowi, nie są od siebie całkowicie niezależne:
właśnie odnalezienie elementów wspólnych — a także relacji między nimi a uczestniczącymi
aktorami — pozwala na uproszczenie modelu i sprawienie, by był bardziej zrozumiały. Za
pomocą relacji rozszerzania można w sposób czytelny wyodrębniać z przypadków użycia
sytuacje wyjątkowe i opcjonalne, lecz często spotykane ciągi zdarzeń, zaś relacja zawierania
pozwala na redukcję redundancji przypadków użycia.

Relacje komunikacyjne między aktorami a przypadkami użycia

Relacje komunikacyjne między aktorami a przypadkami użycia reprezentują przepływ


informacji w ramach każdego z tych przypadków. Aktor inicjujący przypadek użycia powinien
być wyraźnie odróżniony od innych aktorów, z którymi ów przypadek użycia się komunikuje.
Określając aktora inicjującego dany przypadek użycia, określamy jednocześnie aktorów, którzy
nie mają prawa go inicjować; podobnie określając aktorów komunikujących się z konkretnym
przypadkiem użycia, określamy jednocześnie podzbiory funkcjonalności dostępne dla poszcze-
gólnych aktorów. W ten oto sposób, opisując relacje inicjacyjne i komunikacyjne, stwarzamy
punkt wyjścia do zdefiniowania zasad kontroli dostępu do docelowego systemu.
Relacje poszczególnych aktorów z danym przypadkiem użycia określane są zaraz na po-
czątku definiowania tego przypadku. Na rysunku 4.4 widzimy przykład takich relacji dla sys-
temu FRIEND. Stereotyp «initiate® oznacza inicjowanie przypadku użycia przez aktora, ste-
reotyp «part i ci pate» — uczestnictwo w przypadku użycia, bez prawa jego inicjowania.

Relacja rozszerzania między przypadkami użycia

Przypadek użycia może pełnić funkcję rozszerzania innego przypadku użycia, jeśli ten
drugi obejmuje zachowanie reprezentowane przez ten pierwszy (przy spełnieniu określonych
warunków). Załóżmy, że w momencie, gdy F i e l d O f f i c e r zamierza wysłać formularz zgło-
szenia incydentu, urywa się połączenie sieciowe jego laptopa z internetem (na przykład ze
względu na zanik sygnału w tunelu): Fi el dOffi cer musi wówczas zostać poinformowany
(przez aplikację działającą we wspomnianym laptopie), że formularz zgłoszeniowy nie został
4.4. Aktywności związane ze zbieraniem wymagań 177

Rysunek 4.4. Przykład relacji komunikacyjnych między aktorami a przypadkami użycia w systemie
FRIEND. Aktor F i e l d O f f i c e r inicjuje przypadek użycia ReportEmergency, natomiast D i s p a t c h e r
inicjuje przypadki użycia Openlncident i A l l o c a t e R e s o u r c e s . Aktor F i e l d O f f i c e r nie ma więc
prawa do rejestrowania („otwierania") nowego incydentu ani do decydowania o przydzielaniu zaso-
bów potrzebnych do jego obsługi

wysłany i uruchomiona musi zostać określona procedura dotycząca zarówno działań Fi el d


^ O f f i cera, jak i rzeczonej aplikacji. Ta wyjątkowa sytuacja zerwania połączenia modelo-
wana jest jako odrębny przypadek użycia o nazwie Connecti onDown, stanowiący rozszerzenie
przypadku użycia ReportEmergency (patrz rysunek 4.5). Warunki, przy spełnieniu których
inicjowany jest przypadek ConnectionDown, są jego własnymi warunkami wstępnymi i nie
wchodzą w skład przypadku ReportEmergency.

Rysunek 4.5. Przykład relacji rozszerzania. Przypadek użycia ConnectionDown jest rozszerzeniem przy-
padku użycia ReportEmergency. Użycie relacji rozszerzania przyczynia się do uproszczenia przypadku
ReportEmergency i jego skoncentrowania na czynności zasadniczej — powiadamianiu o niebezpieczeństwie

Oddzielenie sytuacji wyjątkowych i opcjonalnych od zasadniczego przypadku użycia ma


co najmniej dwie zalety. Po pierwsze, „bazowy" przypadek użycia staje się krótszy i bardziej
czytelny. Po drugie, rozdzielone zostają dwa różne rodzaje zachowań — zachowania „nor-
malne" i wyjątkowe — co pozwala programistom potraktować oba aspekty funkcjonalności
osobno, na przykład z punktu widzenia optymalizacji (zachowania „normalne" optymalizo-
wane są pod kątem efektywności, zaś zachowania wyjątkowe — pod kątem niezawodności).
Każdy z dwóch przypadków użycia — bazowy i rozszerzający — jest odrębną całością, posia-
dającą własne warunki początkowe i końcowe.

Relacja zawierania między przypadkami użycia

Relacja zawierania umożliwia eliminowanie z przypadków użycia elementów redun-


dantnych, czyli odzwierciedlających powtarzającą się funkcjonalność. Załóżmy, że Di spatcher
w momencie „otwierania" incydentu posługuje się mapą podległego mu obszaru, na przykład
w celu oceny ryzyka stwarzanego przez zaistniały właśnie pożar czy też zidentyfikowania zasobów
178 Rozdział 4. • Zbieranie wymagań

(wozów strażackich) znajdujących się w pobliżu owego pożaru. Szczegóły czynności wykony-
wanych przez aktora Di spatcher w związku z korzystaniem z mapy wyodrębnione zostają
w postaci osobnego przypadku użycia ViewMap, wykorzystywanego przez dwa inne przypadki
użycia — Open Incident i AllocateResources, co p o k a z a n o n a r y s u n k u 4.6.

AllocateResources

Rysunek 4.6. Przykład relacji zawierania między przypadkami użycia. Przypadek ViewMap opisuje
przepływ zdarzeń związanych z przeglądaniem m a p y (przewijanie, powiększanie, wyszukiwanie
obiektów według nazwy i tym podobne) i wykorzystywany jest przez przypadki użycia Openlnci dent
i AllocateResources

Wyodrębnianie wspólnych elementów funkcjonalnych z przypadków użycia ma wiele


zalet, spośród których najważniejszymi są skrócenie opisu oraz zredukowanie redundancji. Ale
uwaga: wyodrębnienie takie ma sens wyłącznie w odniesieniu do elementów powtarzających
się — niepotrzebne mnożenie przypadków użycia czyni specyfikację wymagań mniej czytelną
dla klienta i użytkowników.

Różnice między zawieraniem a rozszerzaniem

Relacje zawierania i rozszerzania są podobnymi konstrukcjami i podobieństwo to może


sprawiać pewien kłopot programiście, który nie ma jeszcze wprawy w modelowaniu [Jacob-
son i in., 1992]. Podstawową różnicą między wspomnianymi relacjami jest kierunek każdej
z nich: dla relacji zawierania zdarzenie wyzwalające zawierany (docelowy) przypadek użycia
opisywane jest w przepływie zdarzeń przypadku nadrzędnego (źródłowego), zaś dla relacji
rozszerzania zdarzenie wyzwalające przypadek rozszerzający (źródłowy) jest jego warunkiem
początkowym. Innymi słowy, każde włączenie przypadku podrzędnego („zawieranego") do
przypadku nadrzędnego („zawierającego") musi być powiązane z określeniem warunku, przy
spełnieniu którego włączany przypadek jest aktywowany. Dla odmiany, w przypadku relacji
rozszerzania przypadki nadrzędne („rozszerzane") pozostają bez zmian, natomiast w przy-
padku rozszerzającym specyfikuje się listę przypadków rozszerzanych za jego pomocą.
Tak więc jeśli jakiś aspekt zachowania systemu powiązany jest ze zdarzeniem występu-
jącym w stosunkowo niewielu przypadkach użycia, kwalifikuje się do wyodrębnienia jako
przypadek dołączany („zawierany") do innych przypadków nadrzędnych. Relacja zawierania
okazuje się odpowiednia dla zdarzeń powszechnie występujących w przypadkach użycia —
takich jak pobieranie nazwy pliku, przeglądanie mapy, wybór opcji i tym podobne.
Jeżeli natomiast dane zachowanie powiązane jest ze zdarzeniem mogącym wystąpić
w dowolnej chwili bądź wystąpienie tego zdarzenia łatwo zakwalifikować jako warunek wstępny,
wspomniane zachowanie nadaje się do modelowania za pomocą przypadku rozszerzającego.
4.4. Aktywności związane ze zbieraniem wymagań 179

Relacja rozszerzania jest bardziej odpowiednia dla zdarzeń wyjątkowych lub zachodzących
sporadycznie — na przykład dla zerwania połączenia, anulowania transakcji czy uruchomie-
nia pomocy kontekstowej.
Ilustracją porównania obu relacji (zawierania i rozszerzania) jest tabela 4.6, w której
widoczne są efekty powiązania ze sobą przypadków użycia ReportEmergency i ConnectionDown.
Relacja zawierania (lewa kolumna) wymaga dołączenia w dwóch miejscach nadrzędnego
przypadku (ReportEmergency) tekstu oznaczającego dołączenie przypadku ConnectionDown,
aktywowanego pod określonym warunkiem. W przypadku relacji rozszerzania przypadek nad-
rzędny pozostaje nietknięty, natomiast w przypadku rozszerzającym (ConnectionDown) spe-
cyfikowany jest zestaw przypadków nadrzędnych podlegających rozszerzaniu („każdy przypa-
dek użycia, w ramach którego może zdarzyć się zerwanie połączenia sieciowego między
Fi el dOff i cerem a Di spatcherem"). Możliwość modelowania nowych zachowań bez potrzeby
ingerowania w tekst istniejących przypadków użycia — dzięki relacji rozszerzania — jest
bardzo cenna, by nie rzec: krytyczna, z punktu widzenia stabilności modelu i ryzyka popeł-
niania błędów. Rozróżnienie między obiema relacjami, jeśli dokonane właściwie, ma pozytywny
wpływ na jakość dokumentacji, ponieważ zmniejsza redundancję przypadków użycia, redukuje
zależności między przypadkami i zmniejsza ryzyko popełniania błędów w sytuacji zmiany
wymagań. Trzeba jednak przyznać, że z perspektywy innych aktywności programistycznych
różnica między wspomnianymi relacjami jest kwestią marginalną.
We właściwym zastosowaniu jednej albo drugiej relacji mogą okazać się pomocne
heurystyki opisane w ramce.

Heurystyki wyboru między relacjami zawierania i rozszerzania


o Wykorzystuj relację rozszerzania do modelowania zachowania opcjonalnego, wyjątkowego
lub zdarzającego się rzadko — takiego jak na przykład awaria wozu strażackiego (sytuacja
wyjątkowa) czy wezwanie dodatkowego wozu strażackiego znajdującego się w pobliżu,
a niepowiązanego bezpośrednio z incydentem (zdarzenia opcjonalne).
® Wykorzystuj relację zawierania do modelowania zachowania współdzielonego między wiele
przypadków użycia.
o Zachowuj jednak umiar w stosowaniu każdej z relacji: nadmierna fragmentacja modelu w postaci
rozmnażania przypadków użycia nie jest zjawiskiem pożądanym — łatwiej czyta się i rozumie
kilka dwu-, trzystronicowych przypadków użycia niż kilkadziesiąt przypadków kilkuwierszowych.

Niezależnie od opisanych różnic, obie relacje służą jednak temu samemu celowi — redu-
kowaniu lub usuwaniu redundancji z modelu, a w konsekwencji zmniejszaniu ryzyka wystą-
pienia niespójności.

4.4.6. Początkowa identyfikacja obiektów modelu analitycznego


Jedną z pierwszych trudności, jakie pojawiają się na początku współpracy programistów i użyt-
kowników, jest niemożność porozumienia się z powodu używania odmiennej terminologii.
Mimo iż programiści z czasem przyzwyczajają się do mówienia językiem użytkowników,
problem pojawia się na nowo, w sytuacji gdy do zespołu dołącza nowy programista. Nieporo-
zumienia pojawiają się zarówno podczas nazywania tych samych rzeczy w zróżnicowany
sposób, jak również wskutek używania tego samego określenia w różnych znaczeniach.
180 Rozdział 4. • Zbieranie wymagań

Tabela 4.6. Ilustracja różnicy między relacjami zawierania i rozszerzania

ReportEmergency (relacja zawierania) ReportEmergency (relacja rozszerzania)

1. [...] 1. [...]
2. [...] 2. [...]
3. Fi el dOf f i cer wypełnia formularz, określając 3. Fi el dOf f i cer wypełnia formularz, określając
typ zdarzenia, stopień zagrożenia, jego typ zdarzenia, stopień zagrożenia, jego
lokalizację i krótki opis sytuacji. Fi el dOf f i cer lokalizację i krótki opis sytuacji. Fi el dOf f i cer
określa także możliwe sposoby pierwszej określa także możliwe sposoby pierwszej
reakcji na zagrożenie. Po wypełnieniu reakcji na zagrożenie. Po wypełnieniu
f o r m u l a r z a Fi el dOf f i cer wysyła go do f o r m u l a r z a Fi el dOf f i c e r wysyła go
systemu. do systemu.
Gdyby zerwane zostało połączenie sieciowe,
wykorzystywany będzie przypadek użycia
ConnectionDown.
4. Jeśli nawiązane jest połączenie sieciowe, 4. System FRIEND powiadamia dyspozytora
system FRIEND powiadamia dyspozytora (Di s p a t c h e r ) o zdarzeniu. Di s p a t c h e r
(Di s p a t c h e r ) o zdarzeniu. Di s p a t c h e r analizuje informacje zawarte w formularzu,
analizuje informacje zawarte w formularzu, po czym tworzy nowy obiekt Inci dent
p o czym tworzy nowy obiekt I n c i d e n t w bazie, realizując p r z y p a d e k użycia
w bazie, realizując przypadek użycia Openlncident. D i s p a t c h e r określa sposób
Openlncident. D i s p a t c h e r określa sposób reakcji i zatwierdza raport.
reakcji i zatwierdza raport
Gdyby zerwane zostało połączenie sieciowe,
wykorzystywany będzie przypadek użycia
ConnectionDown.
5. ...

ConnectionDown (relacja zawierania) ConnectionDown (relacja rozszerzania)


Przypadek użycia ConnectionDown jest
wykorzystywany do rozszerzania każdego
przypadku użycia, w ramach którego może
zdarzyć się zerwanie połączenia sieciowego
między Fi el dOff i cerem a Di spatcherem.

1. Fi el d O f f i c e r i D i s p a t c h e r otrzymują 1. F i e l d O f f i c e r i D i s p a t c h e r otrzymują
informację o zerwaniu połączenia, być może informację o zerwaniu połączenia, być może
połączoną z wyjaśnieniem prawdopodobnej połączoną z wyjaśnieniem prawdopodobnej
przyczyny zerwania (modem poza zasięgiem przyczyny zerwania (modem poza zasięgiem
sieci, na przykład w tunelu). sieci, na przykład w tunelu).
2. Fakt zerwania połączenia rejestrowany jest 2. Fakt zerwania połączenia rejestrowany jest
w systemie; gdy łączność zostanie przywrócona, w systemie; gdy łączność zostanie przywrócona,
następuje próba ponowienia przerwanej następuje próba ponowienia przerwanej
transmisji danych. transmisji danych.

3. Fi el dOf f i cer i Di spatcher nawiązują kontakt 3. Fi el d O f f i c e r i Di spatcher nawiązują kontakt


za pomocą innych środków, na przykład za pomocą innych środków, na przykład
radiotelefonu; Di s p a t c h e r sam w p r o w a d z a radiotelefonu; Di s p a t c h e r sam wprowadza
do systemu i n f o r m a c j e przekazywane m u do systemu informacje przekazywane m u
słownie przez Fi el dOf f i c e r a . słownie przez Fi el dOff i cera.
4.4. Aktywności związane ze zbieraniem wymagań 181

By uniknąć tych nieporozumień i- wypracować czytelną terminologię, programiści dla


każdego przypadku użycia identyfikują obiekty uczestniczące w tym przypadku, opatrując
je znaczącymi nazwami i intuicyjnym opisem oraz katalogując w specjalnym słowniku8. Two-
rzenie tego słownika jest pierwszym krokiem etapu analizy, którą zajmiemy się szczegółowo
w następnym rozdziale.
Wspomniany słownik włączany jest do specyfikacji wymagań jako jej integralna część,
później zwykle dołączany jest do dokumentacji użytkownika systemu. Jego zawartość wymaga
— oczywiście — aktualizacji przy każdej zmianie wymagań. Korzyści z jego stosowania są
wielorakie: nowi programiści już na początku otrzymują spójny zestaw definicji, każda kon-
cepcja nazywana jest jednym określeniem, wspólnym dla programistów i użytkowników, a każde
używane określenie ma ustalone, precyzyjne, oficjalne znaczenie.
Identyfikacja obiektów uczestniczących w przypadkach użycia jest pierwszym krokiem
na drodze do tworzenia modelu analitycznego. Kompletny model analityczny nie nadaje się
raczej do roli środka komunikacji między programistami a użytkownikami, tym ostatnim mogą
być bowiem obce koncepcje obiektowe w ogólności; jednak opisy obiektów modelu (w kate-
goriach oficjalnej terminologii słownika danych) i ich atrybuty widoczne są dla użytkowników
i podlegają ich ocenie. Doskonaleniem modelu analitycznego zajmiemy się w następnym
rozdziale.
W literaturze przedmiotu proponowanych jest wiele heurystyk mających ułatwić iden-
tyfikowanie obiektów — oto kilka z nich.

Heurystyki pomocne w identyfikowaniu obiektów


Wśród obiektów modelu wyróżnić można między innymi:
® terminy, które programiści muszą uzgodnić z użytkownikami w celu rozumienia
przypadków użycia,
® rzeczowniki powtarzające się w przypadkach użycia ( I n c i d e n t ) ,
® encje świata rzeczywistego odzwierciedlone w systemie (Fi el dOf f i cer, Resource),
® procesy świata rzeczywistego odzwierciedlone w systemie (EmergencyOperati onsPl an),
® przypadki użycia (ReportEmergency),
® miejsca generowania i konsumowania danych (Pri n t e r ) ,
® artefakty wchodzące w interakcje z użytkownikami ( S t a t i on),
® wszystkie opisy, które powinny być sporządzone w kategoriach dziedziny aplikacyjnej.

Jeśli dwa przypadki użycia odnoszą się do tej samej koncepcji, odpowiadające sobie
obiekty powinny nazywać się i wyglądać identycznie. Jeśli dwa obiekty o tej samej nazwie nie
odzwierciedlają tej samej koncepcji, należy przynajmniej jedną z tych nazw zmienić, tak by
uwydatnić różnicę między wspomnianymi koncepcjami; dzięki takiej konsolidacji unikniemy
wieloznaczności używanej terminologii. W tabeli 4.7 widoczna jest początkowa postać listy
obiektów uczestniczących w przypadku użycia ReportEmergency.

8
Nazywanym powszechnie „słownikiem danych".
182 Rozdział 4. • Zbieranie wymagań

Tabela 4.7. Wybrane obiekty uczestniczące w przypadku użycia ReportEmergency

Dispatcher Dyspozytor, oficer policji zarządzający wypadkami ( I n c i d e n t ) . D i s p a t c h e r


d o k o n u j e otwierania, d o k u m e n t o w a n i a i zamykania obsługi wypadków,
zgodnie z raportami (EmergencyReport) i inną informacją pochodzącą od
funkcjonariusza (Fi el dOf f i cer). Di s p a t c h e r identyfikowany jest na podstawie
n u m e r u odznaki.
EmergencyReport Początkowy raport o wypadku, s p o r z ą d z o n y przez f u n k c j o n a r i u s z a
(Fi el dOf f i cer) dla dyspozytora (Di spatcher). Raport taki zwykle powoduje
utworzenie nowego obiektu klasy Incident przez dyspozytora. Podstawowymi
elementami raportu są zwykle: stopień zagrożenia, typ wypadku (pożar, wypadek
drogowy, inne zdarzenie), wskazanie miejsca wypadku, krótki opis sytuacji.
FieldOfficer Funkcjonariusz na służbie. Konkretny funkcjonariusz może być w danej chwili
przydzielony do co najwyżej jednego wypadku. Funkcjonariusze identyfikowani
są na podstawie n u m e r u odznaki.
Incident Wypadek, sytuacja wymagająca interwencji funkcjonariusza (Fi el dOf f i cer).
Wypadek może zostać zgłoszony do systemu przez funkcjonariusza lub przez
inną osobę pozostającą na zewnątrz tego systemu. Informacja o wypadku
obejmuje jego opis, sposób reakcji, status (otwarty, zamknięty, udokumentowany),
lokalizację i identyfikację funkcjonariuszy (Fi el dOf f i cer) przydzielonych
do obsługi.

G d y o b i e k t y u c z e s t n i c z ą c e w p o s z c z e g ó l n y c h p r z y p a d k a c h użycia z o s t a n ą j u ż s k o n -
s o l i d o w a n e , p r o g r a m i ś c i m o g ą użyć ich w c h a r a k t e r z e listy k o n t r o l n e j w celu u p e w n i e n i a się,
że z b i ó r o p r a c o w a n y c h p r z y p a d k ó w użycia jest k o m p l e t n y .

Heurystyki w e r y f i k o w a n i a kompletności z e s t a w u p r z y p a d k ó w użycia


w kontekście o b i e k t ó w u c z e s t n i c z ą c y c h

• W którym z przypadków użycia tworzony jest ten konkretny obiekt — kiedy wartości jego
atrybutów wprowadzane są do systemu?
• Którzy aktorzy mają dostęp do informacji zawartej w tym obiekcie?
• Które przypadki użycia mogą modyfikować i (lub) usuwać ten konkretny obiekt — w których
przypadkach użycia następuje edytowanie lub usuwanie informacji zawartej w tym obiekcie?
• Który aktor może inicjować ten konkretny przypadek użycia?
• Czy ten konkretny obiekt w ogóle jest potrzebny — czy chociaż jeden przypadek użycia
zależny jest od zawartej w tym obiekcie informacji?

4.4.7. Identyfikacja wymagań pozafunkcyjnych


W y m a g a n i a p o z a f u n k c y j n e o p i s u j ą te a s p e k t y s y s t e m u , k t ó r e nie są b e z p o ś r e d n i o p o w i ą z a n e
z przejawianą przez ten system funkcjonalnością. M o g ą dotyczyć różnych aspektów systemu,
o d w y g l ą d u interfejsu u ż y t k o w n i k a , p o p r z e z d o p u s z c z a l n y czas reakcji, aż d o z a g a d n i e ń zwią-
z a n y c h z b e z p i e c z e ń s t w e m . D e f i n i o w a n e są w r a z z w y m a g a n i a m i f u n k c j o n a l n y m i , j a k o że
mają r ó w n i e istotny wpływ na tworzenie systemu i jego koszt.
4.5. Zarządzanie zbieraniem wymagań 183

Weźmy jako przykład wyświetlacz mozaikowy, służący kontrolerowi lotu do śledzenia


ruchu samolotów w podległym mu obszarze. Informacja prezentowana na takim wyświetlaczu
stanowi kompilację informacji z wielu radarów oraz baz danych (stąd przymiotnik „mozai-
kowy") i dla każdego samolotu znajdującego się w obserwowanym obszarze wyświetlana jest
jego identyfikacja, prędkość i wysokość, na jakiej się znajduje. Maksymalna liczba samolotów,
jaką system jest wstanie jednocześnie zarządzać, determinuje zarówno wydajność pracy kon-
trolera, jak i koszt całego systemu: gdy jest niewielka, system nadaje się jedynie dla obszarów
o niezbyt dużym natężeniu ruchu, z kolei system zdolny kontrolować wiele samolotów jedno-
cześnie jest i droższy, i bardziej złożony pod względem tworzenia i testowania.
Wymagania pozafiinkcyjne mają to do siebie, że mimo iż dotyczą niekiedy mało uchwyt-
nych (czy sprawiających wrażenie mało istotnych) aspektów systemu, to mogą mieć krytyczny
wpływ na pracę użytkownika. W przypadku wspomnianego wyświetlacza mozaikowego mak-
symalna liczba samolotów, jakie mogą być jednocześnie widoczne, wpływa nie tylko na zło-
żoność i koszt systemu, lecz także na takie (wydawałoby się) drobiazgi jak wielkość ikon re-
prezentujących poszczególne samoloty i sposób opatrywania ich informacją identyfikacyjną,
nie wspominamy tu już o częstotliwości jej odświeżania.
Po zebraniu pewnej porcji wymagań pozafunkcyjnych ich zbiór może się okazać we-
wnętrznie sprzeczny. Od przykładowego zegarka SatWatch wymaga się, by był bardzo dokładny
(co wyklucza potrzebę ręcznego resetowania czy korygowania wskazań) i jednocześnie na tyle
tani, żeby w przypadku awarii kupno nowego nie było problemem finansowym. Jest zrozumiałe,
że większa dokładność zegarków okupiona jest zwykle wyższą ich ceną, klient musi się zatem
zdecydować, które ze sprzecznych wymagań (tani czy dokładny) ma dla niego wyższy priorytet.
Niestety, istnieje niewiele systematycznych metod zbierania wymagań pozafunkcyjnych.
W praktyce analitycy posługują się ich taksonomią (na przykład opisywanym wcześniej sche-
matem FURPS+) w celu stworzenia listy pytań pomagających skupić się klientowi i pro-
gramistom na pozafunkcyjnych aspektach systemu. Ponieważ na tym etapie zidentyfikowani
są już aktorzy, wspomniana lista może być zorganizowana w podziale na role i na tej podstawie
rozdzielona między reprezentatywnych użytkowników. Zaletą takiej listy jest wielokrotna
przydatność — nie tylko na potrzeby aktualnie tworzonego systemu, lecz także przyszłych sys-
temów związanych z tą samą dziedziną aplikacyjną. Ponadto dzięki takiej liście użytkownik
łatwiej może uświadamiać sobie nowe potrzeby (w zakresie wymagań pozafunkcyjnych)
— na przykład w zakresie administrowania systemem. Przykłady pytań kwalifikujących się na
taką listę, wzorowanych na schemacie FURPS+, przedstawione są w tabeli 4.8.
Gdy zbieranie wymagań pozafunkcyjnych zostanie zakończone, następuje ich dosko-
nalenie i identyfikowanie zależności między nimi, a także ewentualnych sprzeczności. Czytel-
ników zainteresowanych szczegółami aktywności tego rodzaju odsyłamy do pracy L. Chun-
ga, B. A. Nixona, E. Yu i J. Mylopoulosa [Chung i in., 1999].

4.5. Zarządzanie zbieraniem wymagań


W poprzedniej sekcji opisaliśmy techniczne aspekty modelowania systemu w kategoriach
przypadków użycia. Przypadki użycia same z siebie nie załatwiają jednak zbierania wymagań;
programiści, nawet wyspecjalizowani w ich tworzeniu, w dalszym ciągu muszą współdziałać
z użytkownikami i uzgadniać swe działania z klientem. W tej sekcji opiszemy sposoby zbiera-
nia informacji od użytkowników i negocjowania porozumień z klientem, a szczególnie:
184 Rozdział 4. • Zbieranie wymagań

Tabela 4.8. Przykładowe pytania ułatwiające identyfikowanie wymagań pozafunkcyjnych

Kategoria Przykładowe pytania


Użyteczność « Jaki jest stopień doświadczenia użytkownika w zakresie obsługi podobnych
• systemów i generalnie w zakresie obsługi komputera?
Jakie standardy interfejsu GUI są znane użytkownikowi?
o W jaką dokumentację należy wyposażyć użytkownika?

Niezawodność • Jak wysoce niezawodny, dostępny i solidny powinien być tworzony system?

(w tym solidność, Czy dopuszczalny jest restart systemu w przypadku jego awarii?
bezpieczeństwo • Jaki stopień utraty danych powinien być tolerowany przez system?
i zabezpieczenia) • W jaki sposób system powinien reagować na sytuacje wyjątkowe?
• Czy od systemu wymaga się szczególnych cech w zakresie bezpieczeństwa
• dla użytkowników i środowiska?
Czy i jakie mechanizmy zabezpieczeń mają być obecne w systemie?

Wydajność • Jak szybko powinien reagować system na akcje użytkownika?



Czy system obejmuje jakieś zadania z krytycznym uwarunkowaniem czasowym?

• Jak wielu użytkowników będzie mógł jednocześnie obsługiwać system?
• Jak duże są magazyny danych w porównywalnych systemach?
Jak wielkie opóźnienie ze strony systemu będzie akceptowalne dla użytkowników?

Wspieralność • Jakie prawdopodobne rozszerzenia systemu przewidywane są w przyszłości?



(w tym łatwość Kto będzie odpowiedzialny za utrzymanie systemu?
utrzymania 0 Czy istnieją plany przenoszenia systemu na inną platformę sprzętową
i przenośność) lub programową?

Implementacja • Czy istnieją konkretne wymagania dotyczące platformy sprzętowej dla systemu?
a Czy istnieją wymagania pod adresem zespołu utrzymania systemu?

Czy istnieją wymagania dotyczące testowania systemu?

Interfejs • Czy system ma współpracować z innymi systemami?



• W jaki sposób dane są eksportowane z systemu i importowane do niego?
Jakie standardy zachowań użytkownika powinny być respektowane przez system?

Operatywność • Kto będzie odpowiedzialny za nadzór nad funkcjonowaniem systemu?

Pakietowanie • Kto będzie instalował system?



• Jak wiele instalacji przewidziano?
Czy istnieją szczególne wymagania lub ograniczenia związane z instalacją?

Prawo • Jakie będą zasady licencjonowania systemu?



Czy awarie systemu powodować będą odpowiedzialność cywilną lub karną?
• Czy w systemie wykorzystywane będą k o m p o n e n t y i (lub) algorytmy,
których używanie wiąże się z opłatami licencyjnymi?

• Negocjowanie specyfikacji z klientem i metodę Joint Application Design (patrz


sekcja 4.5.1),
« Zarządzanie identyfikowalnością (patrz sekcja 4.5.2),
• Dokumentowanie zbierania wymagań (patrz sekcja 4.5.3).
4.5. Zarządzanie zbieraniem wymagań 185

4.5.1. Negocjowanie specyfikacji z klientem:


metoda Joint Application Design
Joint Application Design (JAD) to metoda opracowana w firmie IBM w latach 70. ubiegłego
wieku. Jej efektywność wynika z faktu, że zbieranie wymagań odbywa się w formie jednora-
zowej sesji warsztatowej, w której uczestniczą wszystkie osoby zaangażowane w realizację
projektu. Użytkownicy, klienci, programiści i wyszkolony prowadzący spotykają się w jednym
pomieszczeniu w celu zaprezentowania własnych punktów widzenia i poznania innych oraz
negocjowania i poszukiwania rozwiązań akceptowanych przez wszystkich uczestników. Re-
zultat sesji — dokument JAD — zawiera kompletną specyfikację wymagań obejmującą ele-
menty danych, przepływ pracy i przykładowe zrzuty ekranów. Ponieważ dokument końcowy
opracowywany jest wspólnie przez wszystkich uczestników (czyli również tych, którzy po-
dejmować mogą znaczące decyzje), reprezentuje on porozumienie między użytkownikami,
klientami i programistami, minimalizujące prawdopodobieństwo modyfikowania wymagań
w późniejszych stadiach realizacji projektu. Sesja JAD obejmuje pięć głównych aktywności,
przedstawionych na rysunku 4.7.

ł. Definiowanie projektu. W ramach tej aktywności facilitator JAD, w drodze wywiadu


z menedżerem projektu i klientem, określa cele i zakres projektu. Dane zebrane
w trakcie wywiadu utrwalane są w słowniku menedżera.
2. Badania i wywiady. W ramach tej aktywności facilitator JAD przeprowadza wywiady
z obecnymi i przyszłymi użytkownikami, zbierając informacje na temat dziedziny
aplikacyjnej i konstruując pierwszy zestaw wysokopoziomowych przypadków
użycia. Rozpoczyna także sporządzanie listy problemów, jakie powinny zostać
rozwiązane w trakcie sesji. Rezultatem tej aktywności są agenda sesji oraz wstępna
specyfikacja.
3. Przygotowania. Ta aktywność ma na celu właściwe przygotowanie sesji. Facilitator
JAD sporządza w tym celu dokument roboczy stanowiący pierwszy szkic dokumentu
końcowego, skrypt sesji i ewentualne slajdy bądź wykresy odzwierciedlające wiedzę
zebraną w ramach badań i wywiadów. Formuje także zespół, w skład którego wcho-
dzą: klient, menedżer projektu, wybrani użytkownicy i programiści. Reprezentowane
są więc wszystkie grupy zaangażowane w projekt, wszyscy obecni mają możliwość
podejmowania wiążących decyzji.
4. Sesja. To zasadnicza część JAD. Jest prowadzona przez facilitatora, a jej celem jest
wypracowanie specyfikacji wymagań. Typowa sesja JAD trwa od 3 do 5 dni, w czasie
których zespoły definiują i uzgadniają scenariusze, przypadki użycia i prototypy oraz
makiety interfejsu użytkownika. Wszystkie podejmowane decyzje dokumentowane
są przez pisarza.
5. Dokument końcowy. Końcowa część sesji JAD polega na wypracowaniu dokumentu
końcowego poprzez zmodyfikowanie dokumentu wstępnego na podstawie decyzji
podjętych w trakcie sesji. Dokument końcowy reprezentuje kompletną, uzgodnioną
specyfikację systemu i jest przekazywany każdemu z uczestników sesji do oceny.
Następnie uczestnicy spotykają się i w ciągu 1 - 2 godzin wypracowują ostateczną
wersję dokumentu.
186 Rozdział 4. • Zbieranie wymagań

Rysunek 4.7. Aktywności metodologii JAD. Centralną jej częścią jest Sesja, w ramach której wszyscy
uczestnicy wspólnie wypracowują specyfikację wymagań. Aktywności poprzedzające Sesją mają na celu
zwiększenie jej efektywności. Dokument końcowy, będący wynikiem S e s j i , jest odzwierciedleniem
decyzji podejmowanych na jej forum

Metoda JAD wykorzystywana była przez firmę IBM i kilka innych firm. JAD wykorzy-
stuje zalety dynamiki grupowej w celu usprawnienia komunikacji między uczestnikami i szyb-
szego osiągnięcia konsensusu. Po zakończeniu sesji JAD programiści są bardziej świadomi
potrzeb użytkowników, użytkownicy mają natomiast pojęcie o wielu kompromisach, jakie
przychodzi programistom rozstrzygać. Przyczynia się to do zmniejszenia prawdopodobieństwa
4.5. Zarządzanie zbieraniem wymagań 187

wnoszenia poprawek do projektu w dalszych jego etapach. Jako że sukces metody JAD zasadza
się na dynamice społeczności, uwarunkowany jest w dużej mierze kwalifikacjami facilitatora
prowadzącego cały proces. Więcej szczegółowych informacji na temat metody JAD znaleźć
mogą czytelnicy w pracy J. Wooda i D. Silvera [Wood i Silver, 1989].

4.5.2. Zarządzanie identyfikowalnością


Identyfikowalność to możliwość śledzenia losów każdego z wymagań — genezy (kto wy-
maganie sformułował, w związku z jakimi potrzebami klienta) oraz związku z funkcjonalno-
ścią systemu (które komponenty systemu realizują wymaganie i które przypadki testowe we-
ryfikują tę realizację). Identyfikowalność umożliwia programistom pokazanie, że system jest
kompletny; testerom daje pewność, że system zgodny jest ze wszystkimi wymaganiami, pro-
jektantom dostarcza racjonalizację, zaś personelowi odpowiedzialnemu za utrzymanie pozwala
szacować wpływ wprowadzanych zmian.
Powróćmy do naszego przykładowego zegarka SatWatch. Zgodnie z aktualną specyfika-
cją, wyświetlacz powinien mieć strukturę dwuwierszową, dla wyświetlenia czasu i daty w osob-
nych wierszach. Klient zauważa jednak, że wyświetlane cyfry są zbyt małe, by ich odczytywanie
można było nazwać komfortowym i zmienia wymaganie — wyświetlacz ma być jednowier-
szowy, a przełączanie między czasem a datą ma odbywać się za pomocą dedykowanego przy-
cisku. Względy identyfikowalności nakazują w tym momencie udzielenie odpowiedzi na na-
stępujące pytania:

• Kto jest autorem wymagania, że wyświetlacz ma być dwuwierszowy?


• Czy jakiekolwiek niejawne ograniczenie wymusza takie wymaganie?
• Które komponenty zegarka muszą ulec zmianie wskutek dodania przycisku i zmiany
struktury wyświetlacza?
• Które przypadki użycia należy w związku z tym zmienić i w jaki sposób?

Najprostszym sposobem zapewnienia identyfikowalności jest utrzymywanie odwołań


skrośnych między dokumentami, modelami i fragmentami kodu źródłowego. Poszczególne
elementy — wymagania, komponenty, klasy, operacje, przypadki użycia i tym podobne — po-
winny zostać opatrzone unikalnymi numerami, wówczas każde ze wspomnianych odwołań
będzie miało postać (uporządkowanej) pary numerów, wiążącej elementy źródłowy i docelowy.
Do przetwarzania takich odwołań okazują się wystarczające proste narzędzia w rodzaju arkusza
kalkulacyjnego czy nawet edytora tekstu, jednakże przetwarzanie to jest czasochłonne, pra-
cochłonne i podatne na błędy; mimo to, w małych projektach bardzo szybko daje pozytywne
wyniki.
W dużych projektach pożądane jest jednak używanie specjalizowanych narzędzi bazo-
danowych, częściowo automatyzujących identyfikowanie, edytowanie i łączenie wspomnia-
nych zależności na bardziej szczegółowym poziomie. Przykładami narzędzi tego typu są DOORS
[Telelogic] i RequisitePro [Rational]. Ich stosowanie przyczynia się do redukowania kosztu
utrzymywania identyfikowalności, wiąże się jednak z zainwestowaniem środków w ich zakup,
koniecznością odpowiedniego przeszkolenia uczestników projektu i ograniczeniami dotyczą-
cymi używania innych narzędzi.
188 Rozdział 4. • Zbieranie wymagań

4.5.3. Dokumentowanie zbierania wymagań


Rezultaty zbierania wymagań i ich analizy dokumentowane są w formie dokumentu ana-
lizy wymagań (Requirements Analysis Document, w skrócie RAD). Dokument ten zawiera
kompletny opis systemu w kategoriach stawianych mu wymagań — funkcyjnych i pozafunk-
cyjnych. Stanowi owoc współpracy klienta, użytkowników, menedżera projektu, analityków
(czyli programistów uczestniczących w zbieraniu wymagań) i projektantów (którymi są pro-
gramiści pracujący nad projektem systemu). Pierwsza jego część, obejmująca przypadki użycia
i wymagania pozafunkcyjne, powstaje w fazie zbierania wymagań; formalizacja tej specyfikacji
w kategoriach obiektów modelu dokonywana jest w fazie analizy. Poniżej widoczny jest przy-
kład szablonu RAD używanego w tej książce; elementy wyróżnione kursywą omówione zo-
staną w następnym rozdziale.

Dokument analizy wymagań


1. Wprowadzenie
1.1. Przeznaczenie systemu
1.2. Zakres funkcjonalny systemu
1.3. Cele i warunki powodzenia projektu
1.4. Definicje, akronimy i skróty
1.5. Odwołania
1.6. Podsumowanie
2. System obecnie użytkowany
3. System proponowany
3.1. Streszczenie
3.2. Wymagania funkcyjne
3.3. Wymagania pozafunkcyjne
3.3.1. Użyteczność
3.3.2. Niezawodność
3.3.3. Wydajność
3.3.4. Wspieralność
3.3.5. Implementacja
3.3.6. Interfejs
3.3.7. Pakietowanie
3.3.8. Wymagania prawne
3.4 Modele systemu
3.4.1. Scenariusze
3.4.2. Model przypadków użycia
3.4.3. Model obiektowy
3.4.4. Model dynamiczny
3.4.5. Interfejs użytkownika — ścieżki nawigacyjne i makiety ekranów
4. Słownik

Pierwsza część dokumentu RAD jest wprowadzeniem. Zawiera uzasadnienie powstania


systemu, jego krótką charakterystykę funkcjonalną, opis zakresu funkcjonalnego i wyjaśnienie
kontekstu jego tworzenia (między innymi nawiązanie do deklaracji problemu sporządzonej
4.5. Zarządzanie zbieraniem wymagań 189

przez klienta, odniesienie do istniejących' systemów i studium opłacalności). We wprowadzeniu


znajduje się także omówienie celów systemu i warunków, od których uzależnione jest powo-
dzenie całego przedsięwzięcia.
Druga część — System obecnie użytkowany — opisuje bieżący stan rzeczy: jeśli nowo
tworzony system ma być następcą istniejącego systemu, znajduje się tu opis funkcjonalności
istniejącego systemu i związanych z nim problemów, w przypadku natomiast tworzenia „od
zera" nowego systemu jest to opis istniejącego wsparcia dla problemów, których rozwiązania
oczekuje się od tegoż systemu. I tak na przykład użytkownik klasycznego zegarka zmuszony
jest do ręcznego korygowania wskazania czasu przy przekraczaniu strefy czasowej, a także
w przypadku rozbieżności wskazań z czasem bieżącym (na skutek ograniczonej dokładności
pomiaru w dłuższym okresie). Użytkownik jest istotą omylną: może po prostu zapomnieć
o przestawieniu czasu, a nawet pamiętając o nim, może się pomylić przy ustawianiu nowej
wartości. Zegarek SatWatch rozwiązuje oba te problemy, zapewniając zarówno automatyczne
dostosowywanie wskazania do bieżącej strefy czasowej, jak i bardzo dużą dokładność po-
miaru w całym okresie eksploatacji. Z kolei system FRIEND ma usprawnić obecną procedurę
obsługi incydentów, opartą na papierowych formularzach i łączności radiotelefonicznej między
dyspozytorem a funkcjonariuszami. Skomputeryzowanie systemu powinno w zamierzeniu
zarówno obniżyć koszty jego funkcjonowania, jak zapewnić bardziej rzetelną dokumentację
wypadków.
Część trzecia — System proponowany — poświęcona jest zbieraniu wymagań i modelo-
wi analitycznemu nowego systemu. Jest podzielona na cztery sekcje, obejmujące:

• streszczenie przedstawiające przegląd funkcjonalny systemu,


• wymagania funkcyjne, czyli wysokopoziomowy opis funkcjonalności systemu,
• wymagania pozafunkcyjne, czyli cechy istotne dla użytkownika, acz niezwiązane bez-
pośrednio z funkcjonalnością: użyteczność, niezawodność, wydajność, wspieralność,
implementację, interfejs, operatywność, pakietowanie i wymagania prawne,
• modele systemu, czyli scenariusze, przypadki użycia, model obiektowy i modele dy-
namiczne. W tej sekcji zawarta jest kompletna specyfikacja funkcjonalności systemu,
ilustrowana makietami interfejsu użytkownika i ścieżkami nawigacyjnymi określają-
cymi następstwo poszczególnych ekranów w dialogu z użytkownikiem. Opisy modelu
obiektowego i modelu dynamicznego sporządzane są dopiero na etapie analizy.

Dokument RAD powinno się tworzyć dopiero po ustabilizowaniu modelu przypadków


użycia, czyli wówczas, gdy zminimalizuje się prawdopodobieństwo modyfikowania wymagań.
Te jednak i tak z konieczności podlegać będą nieuchronnym zmianom, które muszą być do-
kumentowane w ramach zarządzania konfiguracją 9 . Dokument RAD w momencie opubliko-
wania staje się dokumentem bazowym i jednocześnie podmiotem zarządzania konfiguracją:
każda z wprowadzanych do niego zmian rejestrowana jest z podaniem autora za nią odpo-
wiedzialnego, daty i krótkiego opisu.

9
Generalnie, sprawdzoną i formalnie zaakceptowaną wersję produktu nazywa się linią bazową. Za-
rządzanie konfiguracją to proces śledzenia i akceptowania zmian wprowadzanych do linii bazo-
wej. Zarządzaniu konfiguracją poświęcony jest rozdział 13.
190 Rozdział 4. • Zbieranie wymagań

4.6. Analiza przypadku — system ARENA


W tej sekcji p o k a ż e m y z a s t o s o w a n i e o p i s y w a n y c h k o n c e p c j i n a p r z y k ł a d z i e r z e c z y w i s t e g o
s y s t e m u o n a z w i e ARENA. R o z p o c z n i e m y o d w s t ę p n e j deklaracji p r o b l e m u d o s t a r c z o n e j p r z e z
klienta, p o c z y m z b u d u j e m y m o d e l p r z y p a d k ó w użycia i zaczątek m o d e l u analitycznego. Przy-
k ł a d y p r e z e n t o w a n e w p o p r z e d n i c h sekcjach m i a ł y raczej c h a r a k t e r ilustracyjny, w tej sekcji
p r z e d m i o t e m naszych r o z w a ż a ń będzie realistyczny p r z y k ł a d rzeczywistego s y s t e m u , k t ó r e g o
p o w s t a w a n i e b ę d z i e m y śledzić w p o s t a c i k r e o w a n i a i u d o s k o n a l a n i a p o s z c z e g ó l n y c h arte-
faktów. Pozwoli t o n a d o s t r z e ż e n i e wielu subtelności, zwykle n i e o b e c n y c h w sztucznie kre-
o w a n y c h p r z y k ł a d a c h i l u s t r a c y j n y c h . W t y m opisie a k r o n i m ARENA ( p i s a n y w całości d u ż y m i
literami) o z n a c z a ć b ę d z i e system w ogólności, p o d c z a s g d y r z e c z o w n i k „ a r e n a " (pisany w ca-
łości m a ł y m i l i t e r a m i ) o d n o s i ć się b ę d z i e d o k o n k r e t n e j i n s t a n c j i s y s t e m u .

4.6.1. Wstępna deklaracja problemu


E f e k t e m pierwszego s p o t k a n i a z k l i e n t e m jest deklaracja p r o b l e m u . Z w r ó ć m y u w a g ę n a
w y s o k o p o z i o m o w y c h a r a k t e r jej treści — n i e jest to jeszcze ten etap, w k t ó r y m u z g a d n i a się
b u d ż e t i t e r m i n y . T w o r z e n i e naszego m o d e l u p r z y p a d k ó w użycia r o z p o c z n i e m y o d zdefinio-
wania aktorów i scenariuszy.

A R E N A — deklaracja problemu

1. P r o b l e m
Popularność internetu i W W W wyzwoliła fenomen kreowania społeczności wirtualnych — grup
ludzi, którzy powiązani są wspólnymi sprawami, zainteresowaniami i tym podobnymi rzeczami,
lecz być może nigdy nie spotkali się i nie spotkają osobiście. Grupy takie mogą mieć charakter
doraźny (jak w przypadku czatu czy rozgrywania turnieju) bądź długotrwały (czego przykładem
są liczne grupy dyskusyjne). Grupy takie mogą być kilkuosobowe, lecz równie dobrze mogą
obejmować tysiące uczestników.

Upowszechnienie internetu i społeczności wirtualnych nie ominęło także obszaru gier kompu-
terowych. Wydostały się one z ciasnych ram pojedynczych komputerów i sieci lokalnych na forum
„globalnej wioski", w której wspomniane społeczności kreują się także w związku właśnie z rozgry-
waniem rozmaitych gier, rozgrywek, pojedynków czy turniejów. Poszczególni gracze informowani są
0 nowościach w zakresie „swojej" gry, mogą organizować, rozgrywać i obserwować rozgrywki,
porównywać wyniki i wymieniać doświadczenia. Producenci gier online zyskują dzięki temu pożytek
w postaci reklamowania swych produktów i ogólnie generowania dochodów w inny sposób.

W tej konstelacji społeczności gier brakuje jednak koordynacji: każda gra (czy też — każdy pro-
ducent) kreuje społeczność opartą na swych własnych zasadach, odmiennej infrastrukturze, odmien-
nych koncepcjach, zapewniając o d m i e n n e sposoby wsparcia dla uczestników. Ta redundancja
1 niespójność ma wiele mankamentów, między innymi w postaci trudności w zakresie uczenia się
nowych zasad, doświadczanej przez graczy dołączających do kolejnych społeczności. Dla producen-
tów oznacza to konieczność budowania infrastruktury „od zera", dla reklamodawców koniecz-
ność kontaktowania się osobno z każdą ze społeczności. Specyfika infrastruktury w poszczególnych
społecznościach utrudnia także budowanie wspólnych doświadczeń.
5.6. Analiza przypadku — system AREIMA 191

2. Cele
Wobec powyższego, stworzenie systemu ARENA ma w zamierzeniu zrealizować następujące cele:
® dostarczenie powszechnej infrastruktury unifikującej operacje często stosowane na scenie gry:
rejestrowanie nowych gier i graczy, organizowanie rozgrywek i śledzenie ich rezultatów;
• dostarczenie kapitanowi ligi środowiska umożliwiającego planowanie liczby i sekwencji
rozgrywek oraz gromadzenie punktów graczy w rankingach;

• stworzenie środowiska umożliwiającego producentom tworzenie nowych gier lub przystosowywanie


istniejących do standardów infrastruktury ARENA;
® dostarczenie infrastruktury dla reklamodawców.

3. Wymagania funkcyjne
W systemie ARENA wyróżnia się pięć typów użytkowników:
• operator, definiujący nowe gry, nowe style rozgrywek (wygrana przez nokaut, mistrzostwa,
najlepszy w serii i tym podobne) i nowe formuły rankingu oraz zarządzający użytkownikami;
® kapitan ligi, definiujący nową ligę, organizujący i anonsujący nowe rozgrywki w ramach ligi,
prowadzący turnieje i ogłaszający zwycięzców;

• gracz, rejestrujący się na scenie, dołączający do ligi, rozgrywający meczę i ewentualnie rezygnujący
z rozgrywki;

• kibic, mający możliwość monitorowania dowolnego meczu i gromadzenia statystyk związanych


z poprzednimi meczami lub poszczególnymi graczami — bez konieczności rejestrowania się
na scenie;
• reklamodawca, któremu umożliwia się umieszczanie reklam, wybór stylu reklamowania
(sponsor turnieju, sponsor ligi), kontrolowanie stanu konta i wycofywanie reklam.

4. Wymagania pozafunkcyjne
• Niski koszt użytkowania. O p e r a t o r musi mieć możliwość instalowania systemu ARENA
i administrowania jego instancją bez potrzeby dokupywania dodatkowego oprogramowania
i bez pomocy administratora systemu operacyjnego.

• Rozszerzalność. Operator musi mieć możliwość dodawania nowych gier, definiowania nowych
stylów rozgrywek i nowych f o r m u ł rankingu. Czynności te mogą wymagać chwilowego
zamknięcia systemu i dodania do niego nowych modułów (na przykład klas w języku Java),
nie mogą jednak powodować konieczności jego modyfikowania.

® Skalowalność. System musi umożliwiać równoległe prowadzenie wielu rozgrywek (powiedzmy 10),
z których każda obejmuje do 64 graczy i kilkuset kibiców.
® Male wymagania dotyczące przepustowości. Gracze muszą mieć możliwość rozgrywania meczów,
dysponując połączeniem analogowym o szybkości 56 Kb/s lub szybszym.

5. Platforma docelowa
® Gracz musi mieć możliwość dostępu do wybranej instancji systemu za pośrednictwem dowolnej
przeglądarki W W W akceptującej cookies. Funkcje administracyjne (dodawanie nowych gier,
definiowanie nowych stylów, definiowanie użytkowników) nie powinny być dostępne za
pośrednictwem przeglądarki W W W .

• System musi działać niezawodnie na dowolnej platformie uniksowej (na przykład MacOS X,
Linux czy Solaris).
192 Rozdział 4. • Zbieranie wymagań

4.6.2. Identyfikacja aktorów i scenariuszy


Stosownie do zdefiniowanych pięciu typów użytkowników, definiujemy pięciu aktorów. Oto
oni: Operator, LeagueOwner (kapitan ligi), Player (gracz), Spectator (kibic) i Advertiser
(reklamodawca). Jako że zasadniczą funkcją systemu ma być organizowanie i przeprowa-
dzanie rozgrywek, zdefiniujemy na początek przykładowy scenariusz organizeTicTacToe
^Tournament 10 (patrz tabela 4.9), który pomoże nam zgłębić tajniki opisanej na razie ogólnie
funkcjonalności. Przez skupienie się na wąskim obszarze systemu lepiej zrozumiemy ocze-
kiwania klienta pokładane w tym systemie i łatwiej wyznaczymy jego granice oraz rodzaje
interakcji między nim a użytkownikami. Istotnie, w związku ze wspomnianym scenariu-
szem nasuwa się cały szereg pytań pod adresem klienta — niektóre z nich prezentujemy
poniżej. Bazując na udzielonych przez klienta odpowiedziach, możemy przystąpić do do-
skonalenia rzeczonego scenariusza.
Kroki 2. i 7. W systemie rejestrują się różni aktorzy: w pierwszym przypadku admini-
strator rejestruje Joego jako kapitana ligi, w drugim gracz sam rejestruje się w systemie.

• Rejestracja wszystkich użytkowników powinna wzorować się na tym samym para-


dygmacie. Kto dostarcza informacji rejestracyjnej i jak jest ona przeglądana, weryfi-
kowana i akceptowana?
• Klient: W krokach 2. i 7. mowa jest o dwóch różnych procesach: rejestrowaniu, w ra-
mach którego nowy użytkownik (gracz lub kapitan ligi) ustanawiają swoją tożsamość,
oraz aplikowaniu, za pomocą którego zarejestrowany gracz zgłasza chęć udziału
w danym turnieju. W procesie rejestracji użytkownik wprowadza swoje dane osobowe
(imię i nazwisko, nick, adres e-mail) oraz informacje o swych preferencjach (między
innymi o typach gier i turniejów, o których chce być informowany). Wprowadzona
informacja jest weryfikowana przez operatora. W procesie aplikowania użytkownik
wskazuje, w którym turnieju ma ochotę uczestniczyć; informacja ta jest uwzględniania
przez kapitana ligi przy planowaniu meczów.
• Gdy operator zweryfikuje i zaakceptuje informację rejestracyjną użytkownika, czy
zarządzanie jego aplikacjami ma się odbywać całkowicie automatycznie?
• Klient: Tak, oczywiście.

Krok 5. Joe wysyła pocztę elektroniczną do różnych społeczności.

• Czy ARENA ma umożliwiać użytkownikom selektywne subskrybowania wybranych


list mailingowych?
• Klient: Tak. Przewidywane są listy związane z ogłaszaniem nowych meczów, nowej ligi,
nowych turniejów i tym podobne.
• Czy ARENA przechowywać będzie profile poszczególnych użytkowników (historię
rozgrywanych i oglądanych meczów, zainteresowania) w celach ogłoszeniowych
i reklamowych?

10
Pod nazwą Tic-Tac-Toe ukrywa się popularna gra w kółko i krzyżyk — przyp. tłum.
5.6. Analiza przypadku — system AREIMA 193

Tabela 4.9. Scenariusz organi zeTi cTacToeTournament dla systemu ARENA

Nazwa scenariusza oraanizeTicTacToeTournament

Aktorzy uczestniczący al i c e : O p e a t o r , .ioe:LeaqueOwner, b i l l : S p e c t a t o r , marv:Plaver

Przepływ zdarzeń 1. Joe, kolega Alice jest entuzjastą gry w kółko i krzyżyk, i zgłasza chęć
zorganizowania rozgrywek w tej grze.
2. Alice rejestruje Joego na scenie jako kapitana ligi.

3. Joe definiuje ligę dla początkujących, do której dołączać mogą wszyscy


gracze. Regulamin tej ligi, dedykowanej grze w kółko i krzyżyk informuje,
że rozgrywki dokonywane będą w stylu „wygrana przez nokaut", czyli
zgodnie z formułą „zwycięzca bierze wszystko".
4. Joe organizuje pierwszy turniej, planowany na następny dzień, dla 16 graczy.

5. Joe anonsuje swój zamiar na kilku forach W W W , wysyła także e-mail


do członków innych społeczności zainteresowanych grą w kółko i krzyżyk.

6. Bill i Mary odczytują e-mail otrzymany od Joego.


7. Mary rejestruje się jako zainteresowana grą, również 19 innych graczy
aplikuje swój udział.
8. Joe akceptuje aplikację 16 graczy, odrzucając aplikacje tych, którzy
zgłosili się najpóźniej.
9. 16 wspomnianych graczy (w tym Mary) otrzymuje e-mailem żetony
umożliwiające wejście do gry oraz informację o czasie jej rozpoczęcia.
10. Pozostali zainteresowani z listy mailingowej gry w kółko i krzyżyk
(w tym Bill) otrzymują powiadomienie o turnieju, wraz z nazwiskami
graczy i h a r m o n o g r a m e m meczów.
11. O d m o m e n t u rozpoczęcia turnieju (zgodnie z terminem ogłoszonym
przez Joego) gracze mają określony czas, by rozpocząć swój mecz; gracz,
który nie zgłosi się w wyznaczonym limicie czasowym, przegrywa
walkowerem.
12. Mary rozgrywa swój pierwszy mecz i wygrywa. Awansuje w turnieju
do następnego etapu, w k t ó r y m rozgrywać będzie mecz z i n n y m
zwycięzcą pierwszej rundy.
13. Bill, oglądając stronę główną gry w kółko i krzyżyk, d o w i a d u j e się
0 zwycięstwie Mary i postanawia obserwować jej n a s t ę p n y mecz.
Wybiera więc ów mecz i obserwuje r u c h y obu graczy. Na ekranie
widzi także ogłoszenia o n a s t ę p n y m meczach turnieju oraz r e k l a m y
producentów gry.

14. Turniej kontynuowany jest aż do wyłonienia ostatecznego zwycięzcy,


którego dorobek zostaje powiększony o łączną liczbę punktów zdobytych
we wszystkich meczach.
15. Wspomniany zwycięzca gromadzi także punkty w rankingu.

16. Joe postanawia urządzić kolejne turnieje w swojej lidze; znani gracze
otrzymają wówczas powiadomienia o terminie rozpoczęcia turnieju
1 o pierwszeństwie przed nowymi graczami.
194 Rozdział 4. • Zbieranie wymagań

• Klient: Tak, lecz powinno się akceptować również niekompletne informacje rejestra-
cyjne. Powinno się zachęcać rejestrującego się użytkownika do wypełnienia ankiety
o jego zainteresowaniach i tym podobnych, lecz nie można go do tego zmuszać pod
rygorem nieprzyjęcia rejestracji. Niezależnie od kompletności zgłoszenia, użytkownik,
rejestrując się, wyraża domniemaną zgodę na otrzymywanie informacji reklamowych.
• Czy profile użytkowników powinny być automatycznie przydzielane do list mailin-
gowych?
• Klient: Nie, zakładamy bowiem, że w naszej społeczności użytkownik sprawować bę-
dzie pełną kontrolę nad swymi subskrypcjami. Ukryte subskrypcje z pewnością nie
wywołają u niego wrażenia, iż kontroluje sytuację.

Krok 13. Bill ogląda statystyki meczów i następny mecz chciałby obejrzeć „na żywo".

• W jaki sposób poszczególni gracze identyfikowani są przez kibiców? Za pomocą


imienia i nazwiska, nicka czy adresu e-mail?
• Klient: Użytkownik w procesie rejestracji decyduje o sposobie swego identyfikowania
dla innych aktorów.
• Czy kibic ma mieć możliwość oglądania byłych meczów?
• Klient: Generalnie powinna istnieć taka możliwość, lecz w stosunku do niektórych gier
(na przykład rozgrywanych w czasie rzeczywistym czy prezentujących bogatą scenerię
3D) można z niej zrezygnować ze względu na duże zapotrzebowanie na zasoby systemu.
• Czy system ARENA powinien zapewniać obsługę gier w czasie rzeczywistym?
• Klient: Tak, ponieważ takie gry stanowią pokaźny udział w naszym rynku. Generalnie
system ARENA powinien zapewniać obsługę gier w najszerszym zakresie, jaki tylko jest
możliwy do zrealizowania.
' [...]

Zauważmy, że głównym celem zadawania pytań klientowi jest zrozumienie jego potrzeb
oraz pozyskanie wiedzy z zakresu dziedziny aplikacyjnej. Gdy tę wiedzę zdobędziemy i spo-
rządzimy pierwszą wersję specyfikacji wymagań, możemy przystąpić do negocjowania z klien-
tem kompromisów między sprzecznymi wymaganiami, na przykład między zakresem funk-
cjonalnym systemu a jego kosztem. Nie spieszmy się jednak — zbyt wczesne przeplatanie
zbierania wymagań z ich negocjowaniem na ogół nie bywa zbyt produktywne.
Gdy pierwszy scenariusz zostanie dopracowany do tego stopnia, że uzgodnione zostaną
z klientem granice systemu (dla tego jednego scenariusza), możemy zająć się ogólnie rozu-
mianym obrazem funkcjonalnym tego systemu. Dokonywać będziemy tego sukcesywnie,
identyfikując wiele krótszych scenariuszy, oddzielnie dla poszczególnych aktorów; nie będą
one jeszcze zbyt szczegółowe, pokryją jednak znaczącą część wspomnianej funkcjonalności
(patrz rysunek 4.8). Gdy napotkamy nieporozumienia i wieloznaczności, konieczne stanie się
udoskonalanie wspomnianych scenariuszy. W naszym przykładzie scenariusze defineKnockOut
*-*-Styl e i i nstal 1 Ti cTacToeGame powinny być uszczegółowione na poziomie porównywalnym
ze scenariuszem organizeTicTacToeTournament z tabeli 4.9.
5.6. Analiza przypadku — system AREIMA 195

Rysunek 4.8. Wysokopoziomowe scenariusze dla systemu ARENA. Kwalifikują się do uszczegółowie-
nia w celu wyjaśnienia niejednoznaczności lub wykrycia nieporozumień

Typowy uszczegółowiony scenariusz zajmuje kilka stron tekstu, dla uniknięcia nieporo-
zumień konieczne jest więc utrzymywanie oficjalnego słownika terminologii, który z jednej
strony zapewnia spójność specyfikacji, z drugiej natomiast wymusza komunikację językiem
użytkownika. Przeglądając scenariusz z tabeli 4.9, łatwo zauważyć, że wyrazy „mecz", „gra",
„turniej" i „liga", choć oznaczają istotne koncepcje dziedziny aplikacyjnej, używane są bez
wyjaśnienia ich znaczenia. W świecie gier stosowane są w różnych kontekstach, aby więc
uniknąć późniejszych komplikacji związanych z tym faktem, konieczny jest precyzyjny opis
ich znaczenia — i od niego właśnie zaczniemy budowanie wspomnianego słownika, którego
zaczątek widoczny jest w tabeli 4.10.
Gdy tylko programiści porozumieją się z klientem co do zakresu funkcjonalnego
systemu, formalizują zdobytą wiedzę w postaci wysokopoziomowych przypadków użycia.

4.6.3. Identyfikacja przypadków użycia


Generalizacja scenariuszy do postaci przypadków użycia stanowi przejście od konkretnych
sytuacji do bardziej ogólnego przypadku. Programiści łączą wspólne elementy funkcjonalne
w pojedyncze przypadki użycia i jednocześnie rozdzielają niepowiązane ze sobą elementy
funkcjonalne między różne przypadki użycia.
Czytając uważnie scenariusz organ i zeTi cTacToeTournament, zauważymy, że opisuje
on szeroki wachlarz zachowań prezentowanych przez różnych aktorów. Przewidując, że
generalizacja (do postaci przypadku użycia OrganizeTournament) w takiej postaci mogłaby
zająć nawet kilkadziesiąt stron, decydujemy się na jego podział między przypadki użycia obej-
rtiujące pojedynczych aktorów i wzajemnie niezależne od siebie.
Rozpoczniemy od dwóch przypadków użycia, związanych z zarządzaniem kontami użyt-
kowników: ManageUserAccounts inicjowanego przez aktora Operator, oraz Regi s t e r inicjo-
wanego przez potencjalnego gracza (Player) lub kapitana ligi (LeagueOwner). Przy okazji
196 Rozdział 4. • Zbieranie wymagań

Tabela 4.10. Początkowa postać słownika roboczego dla systemu ARENA. Sprawowanie kontroli nad
kluczową terminologią i jej definicjami gwarantuje spójność specyfikacji oraz daje pewność, że pro-
gramiści będą porozumiewać się z klientem w jego języku

T e r m i n polski Termin systemu ARENA Znaczenie

Gra Game Współzawodnictwo między określoną liczbą graczy


(gracz reprezentowany jest przez aktora Player),
p r o w a d z o n e zgodnie z określonymi regułami.
W odniesieniu do systemu ARENA termin g r a
odnosi się do fragmentu kodu odpowiedzialnego
za w y m u s z e n i e tychże reguł, śledzenie postępów
każdego gracza i określenie zwycięzcy. Przykładami
gry są szachy oraz gra w kółko i krzyżyk.

Mecz Match Starcie między dwoma (ogólnie — kilkoma) graczami


(PI ayer), zgodnie z regułami gry. W wyniku meczu
wyłoniony zostaje zwycięzca (pozostali uczestnicy
meczów uważani są wtedy za przegranych) bądź
orzeczony remis. Niektóre gry wykluczają orzekanie
remisu.

Turniej Tournament Seria meczów rozgrywanych między zespołami graczy


(PI ayer). Turniej kończy się wyłonieniem jednego
zwycięzcy. Formuła gromadzenia punktów przez
graczy oraz planowanie meczów w turni eju określane
są przez reguły l i g i organizującej ten turniej.
Liga League Społeczność, k t ó r e j celem jest organizowanie
t u r n i e j ów. Regulamin 1 i gi określa grę, w zamiarze
rozgrywania której została założona, oraz
obowiązujący s t y l t u r n i e j u . Gracze ( P l a y e r )
zarejestrowani w danej 1 i dze gromadzą punkty,
zgodnie z f o r m u ł ą E x p e r t R a t i n g zdefiniowaną
w regulaminie l i g i — i tak na przykład członka
1 i g i nowicjuszy szachowych obowiązuje inna
f o r m u ł a E x p e r t R a t i n g niż członka l i g i
doświadczonych szachistów.

Styl turnieju TournamentStyle Liczba rozgrywanych meczów i ich sekwencja dla


danego zbioru graczy (PI ayer). Przykładem może
być styl „każdy z każdym", zgodnie z którym każdy
gracz spotyka się d o k ł a d n i e jeden raz z każdym
z pozostałych.

i d e n t y f i k u j e m y k o l e j n e g o a k t o r a — Anonymous, r e p r e z e n t u j ą c e g o p o t e n c j a l n e g o u ż y t k o w n i k a ,
k t ó r y n i e p o s i a d a jeszcze k o n t a w systemie ARENA. P o d o b n i e r o z d z i e l a m y f u n k c j o n a l n o ś ć , zwią-
z a n ą z o g l ą d a n i e m byłych m e c z ó w o r a z z z a r z ą d z a n i e m p r o f i l a m i u ż y t k o w n i k ó w , m i ę d z y d w a
k o l e j n e p r z y p a d k i użycia: B r o w s e T o u r n a m e n t H i s t o r y i M a n a g e O w n P r o f i l e , i n i c j o w a n e p r z e z
a k t o r ó w ( o d p o w i e d n i o ) S p e c t a t o r i PI a y e r . W r e s z c i e , w celu dalszego s k r ó c e n i a p r z y p a d k u
użycia Organ i z e T o u r n a m e n t , w y d z i e l a m y p r z y p a d e k użycia D e f i neLeague, w r a m a c h k t ó r e g o
a k t o r i n i c j u j ą c y LeagueOwner m o ż e o r g a n i z o w a ć wiele t u r n i e j ó w , z g o d n i e z r e g u ł a m i swej ligi.
P r z e w i d u j ą c , że instalacja n o w y c h gier i d e f i n i o w a n i e n o w y c h stylów t u r n i e j u w y m a g a ć będzie
5.6. Analiza przypadku — system AREIMA 197

podobnych kroków ze strony aktora Operator, konsolidujemy całą funkcjonalność związaną


z instalowaniem nowych komponentów w przypadku użycia ManageComponents inicjowanym
przez tegoż aktora. Opisaną konfigurację przypadków użycia i aktorów przedstawiono na ry-
sunku 4.9 i w tabeli 4.11. Warto zauważyć, że sam diagram byłby mało czytelnym zapisem funk-
cjonalności systemu. Pełni on więc raczej funkcję indeksu do zamieszczonych poniżej opisów.

Rysunek 4.9. Diagram wysokopoziomowych przypadków użycia dla systemu ARENA

Kolejny krok to — oczywiście — wypełnienie treścią poszczególnych pól każdego z wy-


mienionych przypadków użycia: listy aktorów uczestniczących, warunków wstępnych i koń-
cowych oraz przepływu zdarzeń. Wysokopoziomowa postać przypadku użycia Organize
^Tournament widoczna jest w tabeli 4.12.
Nieprzypadkowo wszystkie kroki przepływu zdarzeń odzwierciedlają akcje wykonywane
przez aktorów, wysokopoziomowe przypadki użycia opierają się bowiem głównie na zadaniach
aktorów. Szczegółowa interakcja aktorów z systemem i decyzje na temat jego granic zostają
odłożone do fazy precyzowania przypadków użycia. Umożliwia to opisanie najpierw dziedziny
aplikacyjnej między innymi w kategoriach tego, jak poszczególni aktorzy współdziałają, by re-
alizować swoje cele.
W przypadku użycia przedstawionym w tabeli 4.12 uczestniczy czworo aktorów: League
"-•Owner odpowiedzialny za całokształt turniejów w swojej lidze, Adverti ser sponsorujący
ligę lub konkretny turniej, PI ayer rozgrywający mecz i S p e c t a t o r kibicujący rozgrywkom.
Najbardziej zagadkowa wydaje się rola sponsora reklamodawcy (nie jest ona wystarczająco
jasno reprezentowana na diagramie z rysunku 4.8), od niej więc zaczniemy szczegółowe roz-
ważania, niejako odzwierciedlając przy okazji fakt, że wszelkie kwestie związane ze sponsorin-
giem muszą być załatwione, zanim potencjalni gracze będą mogli zgłaszać swój udział
w turnieju. W trakcie dyskusji z klientem decydujemy się także na obsługę sponsoringu kon-
kretnego turnieju (obok dotychczasowego sponsorowania ligi), uzgadnianego na początku
turnieju. Z jednej strony, wymaga to dodawania nowych sponsorów do systemu, z drugiej,
umożliwia sponsorowi wykorzystywanie swych własnych zasobów, ponadto pozwala na lepszy
wybór banerów reklamowych do prezentacji.
198 Rozdział 4. • Zbieranie wymagań

Tabela 4.11. Opis przypadków użycia z rysunku 4.9

Przypadek użycia Opis

Register Aktor Anonymous inicjuje ten przypadek użycia jako anonimowy


użytkownik w celu zarejestrowania własnego konta w charakterze
gracza ( P l a y e r ) lub kapitana ligi (LeagueOwner). Rejestracja taka jest
niezbędna do wzięcia udziału w turnieju lub założenia ligi. Kibice
( S p e c t a t o r s ) nie muszą rejestrować się w systemie.
ManageUserAccounts O p e r a t o r akceptuje rejestrację gracza (PI a y e r ) lub kapitana ligi
(LeagueOwner), usuwa wybrane konta i współdziała z użytkownikami
w celu uzupełnienia ich informacji rejestracyjnych.
ManageComponents O p e r a t o r instaluje nowe gry i definiuje nowe style turniejów
(TournamentStyl e). Ten przypadek użycia jest generalizacją scenariuszy
d e f i neKnockOutSty1e i i n s t a l 1 TicTacToeGame.
DefineLeague LeagueOwner zakłada nową ligę. Ten przypadek użycia generalizuje
początkowe kroki scenariusza organi zeTi cTacToeTournament.
OrganizeTournament LeagueOwner organizuje i ogłasza nowy turniej, akceptuje
aplikacje graczy, zarządza harmonogramem meczów i decyduje
o rozpoczynaniu poszczególnych turniejów. W ramach turnieju gracze
(PI ayer) rozgrywają mecze, obserwowane przez kibiców ( S p e c t a t o r ) .
Po zakończeniu każdego z t u r n i e j ó w n a s t ę p u j e aktualizacja
punktacji na k o n t a c h graczy. Ten p r z y p a d e k użycia stanowi
generalizację scenariusza organi zeTi cTacToeTournament.
ManageAdverti sements Reklamodawca (Adverti ser) dokonuje upłoadu banerów i wykonuje
funkcje sponsora ligi lub turnieju. Ten przypadek użycia to generalizacja
scenariusza sponsorTi cTacToeBegi nnersLeague.

ManageOwnProfile Gracze (PI ayer) zarządzają swymi subskrypcjami do list mailingowych


oraz odpowiadają na oferty i ankiety reklamodawców.
BrowseTournamentHi s t o r y Kibice ( S p e c t a t o r ) przeglądają statystyki t u r n i e j ó w i statystyki
poszczególnych graczy, odtwarzając zakończone mecze. Ten przypadek
jest generalizacją scenariusza analyzeTi cTacToeTournament.

P r z y p a d e k użycia z tabeli 4.12 jest w y n i k i e m p o d z i a ł u s c e n a r i u s z a o r g a n i zeTi cTacToe


^ T o u r n a m e n t n a sześć k r o k ó w , k t ó r e tak n a p r a w d ę s t a n o w i ą o d w o ł a n i a (na zasadzie relacji
zawierania) d o szczegółowych p r z y p a d k ó w użycia. Stosując k o n s e k w e n t n i e to p o s t ę p o w a n i e
d o w s z y s t k i c h w y s o k o p o z i o m o w y c h p r z y p a d k ó w u ż y c i a , z i d e n t y f i k u j e m y w s z y s t k i e relacje
m i ę d z y a k t o r a m i , k t ó r e m u s z ą zostać o d z w i e r c i e d l o n e w systemie. E f e k t e m tych działań będzie
s u m a r y c z n y opis s y s t e m u z r o z u m i a ł y n a w e t dla k a ż d e g o n o w e g o uczestnika dołączającego d o
projektu.

4.6.4. Doskonalenie przypadków użycia i identyfikacja relacji


D o s k o n a l e n i e p r z y p a d k ó w użycia p o p r z e z w z b o g a c a n i e ich w szczegóły u m o ż l i w i a p r o g r a -
m i s t o m precyzyjne zdefiniowanie informacji w y m i e n i a n e j z a r ó w n o m i ę d z y aktorami, jak i mię-
dzy a k t o r a m i a s y s t e m e m . Szczegółowy p r z y p a d e k użycia m o ż e przyczynić się także d o o d -
krycia a l t e r n a t y w n y c h d r ó g p r z e p ł y w u z d a r z e ń o r a z z i d e n t y f i k o w a n i a n o w y c h wyjątków, k t ó r e
muszą być obsługiwane przez system.
5.6. Analiza przypadku — system AREIMA 199

Tabela 4.12. Przykład wysokopoziomowego przypadku użycia

Nazwa przypadku użycia OrganizeTournament

Aktorzy uczestniczący LeagueOwner — inicjuje przypadek użycia.


Adverti ser, PI ayer i Spectator — komunikują się z przypadkiem użycia.
Przepływ zdarzeń 1. LeagueOwner organizuje turniej (Tournament), zabiega o sponsoring
u reklamodawców (Adverti sers) i ogłasza rozpoczynanie turniejów.
Dołączony przypadek użycia AnnounceTournament.
2. Gracze (PI ayer) aplikują do udziału w turnieju (Tournament).
Dołączony przypadek użycia ApplyForTournament.
3. LeagueOwner przetwarza aplikację potencjalnych graczy (PI a y e r )
i przyporządkowuje ich do odpowiednich meczów.
Dołączony przypadek użycia ProcessAppl i c a t i o n s .
4. LeagueOwner rozpoczyna turniej (Tournament).
Dołączony przypadek użycia Ki ckoffTournament.
5. Gracze (PI ayer) rozgrywają mecze zgodnie z harmonogramem,
kibice ( S p e c t a t o r ) obserwują te mecze.
Dołączony przypadek użycia PI ayMatch.
6. LeagueOwner ogłasza zwycięzcę i archiwizuje dane turnieju (Tournament).
Dołączony przypadek użycia Archi veTournament.

Warunki wstępne • LeagueOwner jest zalogowany do systemu ARENA.

Warunki końcowe • LeagueOwner zarchiwizował dane turnieju w archiwum systemu ARENA,


zwycięzca zyskał nowe punkty na swym koncie
lub
• LeagueOwner anulował turniej, status poszczególnych graczy w lidze
pozostał niezmieniony.

Aby zbytnio nie komplikować naszego opisu, nie przedstawimy tu kompletnego pro-
cesu uszczegółowiania; ograniczymy się do jednego szczególowego przypadku użycia dla każ-
dego z kroków wysokopoziomowego przypadku użycia Organi zeTournament i w rezultacie
otrzymamy diagram przedstawiony na rysunku 4.10. Następnie zajmiemy się przypadkiem
użycia AnnounceTournament, przedstawionym w tabeli 4.13 oraz związanymi z nim sytuacja-
mi wyjątkowymi, zobrazowanymi schematycznie na rysunku 4.11. Doskonalenie pozostałych
przypadków użycia odbywać się będzie w podobny sposób.
Na rysunku 4.10 wszystkie przypadki użycia inicjowane są przez aktora LeagueOwner,
z wyjątkiem przypadków ApplyForTournament i PI ayMatch inicjowanych przez aktora gracza
(PI ayer). Reklamodawca (Adverti ser) uczestniczy w przypadku użycia AnnounceTournament,
zaś kibic (Spectator) bierze udział w przypadkach AnnounceTournament i PI ayMatch. Gracz
(Player) uczestniczy we wszystkich przypadkach użycia stanowiących udoskonalenie
przypadku Organi zeTournament. By nie zaciemniać czytelności i tak już skomplikowanego
diagramu, zrezygnowaliśmy z jawnego przedstawienia relacji « i n i t i a t e s wiążącej aktora
LeagueOwner z uszczegółowionymi przypadkami użycia; używając narzędzi UML, musimy
jednak pamiętać o ich uwzględnieniu. W tabeli 4.13 widoczny jest kolejny szczegółowy
przypadek użycia — AnnounceTournament.
200 Rozdział 4. • Zbieranie wymagań

Rysunek 4.10. Szczegółowy przypadek użycia jako efekt udoskonalenia przypadku Organi zeTournament

Poszczególne kroki przypadku użycia w tabeli 4.13 odzwierciedlają szczegółowo ko-


munikację między aktorami a systemem. Zauważmy, że nie ma w nich mowy o jakichkolwiek
szczegółach interfejsu użytkownika (układzie formularzy, przyciskach, kompozycji okien na
stronie W W W i tak dalej). Znacznie łatwiej będzie bowiem zaprojektować interfejs użytkownika
później, gdy znane już będą intencje i zakres odpowiedzialności poszczególnych aktorów
— właśnie przypisywanie (lub odkrywanie) owych intencji i odpowiedzialności stanowi
sedno fazy doskonalenia przypadków użycia. Opisując szczegółowo kroki przypadku użycia
AnnounceTournament, programiści wraz z klientem podejmują dalsze decyzje dotyczące gra-
nicy systemu i tak:

• wprowadzone zostają daty graniczne zgłaszania chęci udziału w turnieju i przepro-


wadzania wchodzących w jego skład meczów (krok 3. w tabeli 4.13), umożliwia to
zamknięcie każdego turnieju w rozsądnych granicach czasowych;
• reklamodawcy określają w swych aplikacjach, czy są zainteresowani wyłącznym
sponsoringiem, czy też nie, umożliwia to kapitanowi ligi bardziej precyzyjny wybór
sponsorów (krok 4.);
• komunikacja z potencjalnymi sponsorami zarówno w zakresie aplikowania, jak i na-
liczania opłat jest wysoce zautomatyzowana, co rodzi określone wymagania prawne
pod adresem systemu, opisane w polu Wymagania jakościowe przypadku użycia.
5.6. Analiza przypadku — system AREIMA 201

Tabela 4.13. Przykład szczegółowego przypadku użycia

Nazwa przypadku użycia AnnounceTournament


Aktorzy uczestniczący LeagueOwner — inicjuje przypadek użycia.
PI ayer, Adverti ser i Spectator — komunikują się z przypadkiem użycia.
Przepływ zdarzeń I. LeagueOwner wysyła żądanie zorganizowania nowego turnieju.
2. System sprawdza, czy LeagueOwner nie wykorzystał już
przysługującego m u limitu liczby turniejów w lidze lub na
arenie; jeśli mieści się w limicie, system wysyła mu formularz
do wypełnienia.
3. LeagueOwner wpisuje w formularzu swą nazwę, początkową i końcową
datę zgłaszania graczy do turnieju, początkową i końcową datę
rozgrywania meczów w ramach turnieju i maksymalną liczbę graczy
biorących udział w turnieju.
4. System wysyła do kapitana ligi (LeagueOwner) zapytanie
o ewentualny sponsoring wyłączny turnieju i w przypadku
decyzji o takim sponsoringu wyświetla mu listę reklamodawców
(Adverti s e r ) wyrażających chęć takiego sponsoringu.
5. Jeśli LeagueOwner zdecyduje o poszukiwaniu konkretnego
sponsora wyłącznego, wybiera kilka pozycji z proponowanej listy.
6. System powiadamia wybranych sponsorów o nadchodzącym
turnieju i o wysokości ryczałtowej opłaty (flatfee) wyłącznego
sponsoringu.
7. System przekazuje kapitanowi ligi odpowiedzi otrzymane
od zainteresowanych sponsorów.
8. Jeżeli istnieją zainteresowani potencjalni sponsorzy, LeagueOwner
wybiera jednego z nich.
9. System rejestruje dokonany wybór nazwy wyłącznego sponsora
i obciąża k o n t o wskazanego reklamodawcy opłatą
sponsoringową. O d tej chwili w reklamach podczas turnieju
pojawiać się będą banery tylko tego jednego sponsora.
10. Jeżeli nie wyłoniono wyłącznego sponsora — z braku chętnych
lub rezygnacji kapitana ligi z wyboru — w czasie turnieju
wyświetlane będą banery wybierane losowo spośród materiałów
wszystkich zarejestrowanych sponsorów, których konta
obciążone zostaną wynikającymi stąd opłatami proporcjonalnymi.
11. Gdy załatwione zostaną kwestie sponsoringu, system wyświetla
listę graczy, kibiców i reklamodawców zainteresowanych
nowym turniejem.
12. LeagueOwner wybiera osoby, które zostaną powiadomione o nowym
turnieju.
13. System tworzy stronę główną nowego turnieju stanowiącą
punkt startowy dla graczy zamierzających zgłosić swój
udział i kibiców zainteresowanych oglądaniem meczów.
14. Gdy nadejdzie data rozpoczęcia aplikacji, system powiadamia
o tym wszystkich zainteresowanych użytkowników, przesyłając
im link do strony głównej turnieju. Gracze mogą zgłaszać
swój udział aż do ustalonego m o m e n t u (przypadek użycia
Apply ForTournament).
202 Rozdział 4. • Zbieranie wymagań

Tabela 4.13. Przykład szczegółowego przypadku użycia — ciąg dalszy

Warunki wstępne 0 LeagueOwner jest zalogowany do systemu ARENA.

Warunki końcowe « Wybrano styl sponsorowania turnieju: albo wyświetlanie banerów


pochodzących od wyłącznego sponsora, albo losowe wyświetlanie
banerów zarejestrowanych w bazie systemu.
• Potencjalni gracze zostali powiadomieni o nadchodzącym turnieju
i mogą składać zgłoszenia uczestnictwa.
o Potencjalni kibice otrzymali powiadomienie o nowym turnieju i czasie
jego rozpoczęcia.
• Strona główna turnieju jest publicznie dostępna, dzięki czemu może być
zlokalizowana przez każdego i n t e r n a u t ę bądź to za p o m o c ą
wyszukiwarek, bądź też za pośrednictwem strony głównej systemu
ARENA.

Wymagania jakościowe • Komunikacja z reklamodawcami — przyjmowanie ofert i odpowiedzi


na oferty — wymaga bezpiecznego uwierzytelniania, stanowi bowiem
podstawę do naliczania opłat.
• Reklamodawcy powinni mieć możliwość wycofania się z zawartej
umowy sponsoringowej, w okresie wyznaczonym przez lokalne
uregulowania prawne w stosunku do zdalnych transakcji.

Powyższe decyzje stanowią efekty uzgodnień z klientem, co jest o tyle istotne, że różni
klienci, funkcjonujący w różnych realiach, mogą odmiennie rozstrzygać rozmaite kompromisy
między sprzecznymi wymaganiami. Przykładowo pełna automatyzacja obsługi sponsoringu,
wraz z bezpiecznym uwierzytelnianiem, skutkuje systemem bardziej złożonym, więc droższym.
Tańszą alternatywą może być wybór sponsorów za pomocą e-maili, lecz odbieranie ich zobo-
wiązań drogą telefoniczną: system będzie wówczas prostszy, bardziej złożone stają się nato-
miast działania kapitana ligi. Przy rozstrzyganiu takich dylematów decydujący głos ma klient,
zdający sobie — oczywiście — sprawę z wpływu podjętej decyzji na koszt realizacji i termin
dostarczenia systemu.
Z każdym przypadkiem użycia wiążą się określone sytuacje wyjątkowe, które można zi-
dentyfikować, analizując każdy krok z właściwą podejrzliwością. Opiszemy krótko sytuacje
wyjątkowe, jakie mogą wystąpić w związku z przypadkiem użycia AnnounceTournament, który
tym samym rozszerzony zostaje (w sensie relacji «extend») o przypadki użycia reprezentujące
owe sytuacje (patrz rysunek 4.11 i tabela 4.14).
Zwróćmy uwagę, że nie wszystkie z opisanych sytuacji są do siebie podobne — ich
zróżnicowane typy predestynują je do rozwiązywania w różnych fazach realizacji projektu.
Są zatem wśród nich wyjątki spowodowane wyczerpaniem zasobów (MaxNumberOfTournaments
^•Exceeded), błędnymi danymi wejściowymi (Inval idDate, NamelnUse) i ograniczeniami
z zakresu dziedziny aplikacyjnej (Adverti serCredi tExceeded, NoMatchi ngSponsorFound).
Obsługę wyjątków związanych z zasobami najlepiej załatwia się na etapie projektu systemu,
dopiero wówczas bowiem wiadome jest, które z zasobów podlegają jakim ograniczeniom i jak
najefektywniej rozdzielać je między użytkowników. Poprawność wprowadzanych danych to
z kolei kwestia interfejsu użytkownika — programiści decydują o tym, w którym momencie
przeprowadzać kontrolę wprowadzonych danych, w jakiej postaci wyświetlać związane z tym
5.6. Analiza przypadku — system AREIMA 203

Rysunek 4.11. Sytuacje wyjątkowe związane z przypadkiem użycia AnnounceTournament. Diagram


przypadków użycia jest tu taki sam jak na rysunku 4.10

Tabela 4.14. Opis sytuacji wyjątkowych z rysunku 4.11

AdvertiserCreditExceed Reklamodawca (Adverti s e r ) wykorzystał swe środki na


koncie; system usuwa go z listy potencjalnych sponsorów.
InvalidDate LeagueOwner wprowadził niepoprawną datę, system żąda jej
ponownego wprowadzenia.
MaxNumberOfTournamentsExceed LeagueOwner wykorzystał limit przysługującej m u liczby
turniejów; przypadek użycia AnnounceTournament zostaje
zakończony.
NamelnUse LeagueOwner wprowadził nazwę turnieju istniejącą już
w systemie; system żąda p o n o w n e g o wprowadzenia
unikalnej nazwy.
NoMatchi ngSponsorFound Brak chętnych do wyłącznego sponsoringu; system pomija
k r o k wyboru wyłącznego sponsora, w trakcie t u r n i e j u
wyświetlane będą banery wybierane losowo z bazy systemu.

komunikaty i jak minimalizować a priori prawdopodobieństwo pomyłek ze strony użyt-


kowników. Trzecia kategoria wyjątków — związanych bezpośrednio z dziedziną aplikacyjną —
powinna jak najwcześniej zaprzątać uwagę klienta i programistów, dla tych ostatnich wspomnia-
ne wyjątki często nie są oczywiste, a ich przeoczenie skutkuje później kosztownym rewidowa-
niem pracy już wykonanej. Identyfikację wyjątków tej kategorii należy przeprowadzić przez
szczególnie wnikliwą analizę poszczególnych kroków przypadku użycia, przy udziale
klienta lub eksperta z dziedziny aplikacyjnej.
Wiele sytuacji wyjątkowych może być modelowanych zarówno jako przypadek użycia
(Adverti serCreditExceeded), jak i w postaci wymagania pozafunkcyjnego („Reklamodawca
może zadłużyć się tylko do ustalonego limitu, uzgodnionego z operatorem podczas rejestracji").
Druga z tych reprezentacji jest bardziej odpowiednia, gdy jako globalne ograniczenie stosuje
się do wielu przypadków użycia; pierwsza bardziej nadaje się do zdarzeń mających związek tylko
z pojedynczymi przypadkami użycia (NoMatchi ngSponsorFound).
Tworzenie szczegółowych przypadków użycia, z uwzględnieniem wszystkich sytuacji
wyjątkowych, to zasadnicza część wysiłku związanego ze zbieraniem wymagań. W idealnym
przypadku programiści powinni zamknąć zbiór wszystkich przypadków użycia i wyjaśnić
204 Rozdział 4. • Zbieranie wymagań

wszelkie wątpliwości związane z dziedziną aplikacyjną przed uzgodnieniem szczegółów pro-


jektu i przystąpieniem do realizacji systemu. W praktyce prawie nigdy nie bywa tak różowo:
w przypadku dużych systemów programiści tworzą ogrom dokumentacji, dla której zapew-
nienie bezwzględnej spójności jest niesamowicie trudne, jeżeli nie niemożliwe. Co gorsza,
zbieranie wymagań w związku z dużymi projektami wymaga bieżącego finansowania, pochła-
nia bowiem duże zasoby zarówno po stronie klienta, jak i po stronie firmy programistycznej.
Co więcej, przedwczesne zakończenie zbierania wymagań nie wróży nic dobrego na przyszłość
— oznaczać może zmaganie się ze zmianami w ramach licznych przypadków użycia wskutek
odkrywania nowych faktów z dziedziny aplikacyjnej. Liczba i zakres szczegółowości przypad-
ków użycia to kwestie zaufania i ekonomii: klient i programiści powinni wypracować wspól-
nie na tyle dobre rozumienie systemu, by odpowiedzialnie określić budżet projektu i harmo-
nogram jego realizacji, a także strategię rozwiązywania przyszłych, nieprzewidzianych trudności
związanych ze zmianami wymagań, budżetem czy harmonogramem.
Omawiając wymagania systemu ARENA, skupiliśmy się na szczegółach interakcji an-
gażującej graczy (PI ayer) i reklamodawców (Adverti ser), ta interakcja pełni bowiem krytycz-
ną rolę z perspektywy generowania zysków. Przypadki użycia związane z administrowaniem
systemu, instalowaniem nowych gier i definiowaniem nowych stylów rozgrywek kwalifikują
się do późniejszego omówienia, bo obejmują więcej szczegółów technicznych bliższych dzie-
dzinie realizacyjnej.

4.6.5. Identyfikacja wymagań pozafunkcyjnych


Wymagania pozafunkcyjne pochodzą z rozmaitych źródeł. W deklaracji problemu systemu
ARENA określone zostały wymagania z zakresu wydajności i implementacji. Podczas uszczegó-
łowiania przypadku użycia AnnounceTournament zidentyfikowaliśmy kolejne, tym razem o cha-
rakterze prawnym, związane ze sposobem rozliczania z reklamodawcami. Z kolei przy analizie
sytuacji wyjątkowych natrafiliśmy na kolejne wymaganie związane ze zdolnością kredytową
reklamodawcy. I choć w ten sposób uwzględniliśmy pewną liczbę ograniczeń pozafunkcyj-
nych, nie możemy mieć pewności, że wzięliśmy pod uwagę wszystkie istotne dla systemu. By
taką pewność uzyskać, odwołamy się do modelu FURPS+, opisanego w sekcji 4.3.2 (każda inna
systematyczna taksonomia byłaby zresztą równie dobra w tej roli). W tabeli 4.15 widoczna jest
lista kontrolna wymagań pozafunkcyjnych, jakie udało nam się zidentyfikować w związku
z uszczegółowianiem przypadku użycia AnnounceTournament.

4.6.6. Wnioski
W tej sekcji opracowaliśmy kilka przypadków użycia i stworzyliśmy zaczątki obiektowego
modelu analitycznego, bazowaliśmy przy tym na deklaracji problemu dostarczonej przez klienta.
Scenariusze i pytania zadawane klientowi posłużyły jako narzędzia do wyjaśniania wątpliwości
i niejednoznaczności oraz wykrywania brakującej informacji. Zebraliśmy także kilka wymagań
pozafunkcyjnych. Na tej podstawie możemy już wyciągnąć kilka ważnych wniosków. Oto one.

• Zbieranie wymagań wymaga nieustannego patrzenia na problem z różnych perspek-


tyw (modelu nisko- i wysokopoziomowego, interesów klienta i interesów programisty,
encji i aktywności i tym podobnych).
4.7. Literatura uzupełniająca 205

Tabela 4.15. Skonsolidowana lista wymagań pozafunkcyjnych dla systemu ARENA, jako pochodna
pierwszej szczegółowej wersji przypadku użycia AnnounceTournament

Kategoria Wymagania pozafunkcyjne

Użyteczność • Kibice muszą mieć możliwość obserwowania postępu meczu, bez konieczności
uprzedniej rejestracji i bez uprzedniej znajomości rozgrywanych turniejów.

Niezawodność • Awaria systemu wskutek błędu w oprogramowaniu może doprowadzić do


przerwania co najwyżej jednego turnieju, przy niezakłóconym przebiegu
e
pozostałych.
LeagueOwner powinien mieć możliwość wznowienia przerwanego turnieju,
przy czym dopuszcza się utratę co najwyżej ostatniego posunięcia
gracza w każdym przerwanym meczu.

Wydajność • System musi zapewniać jednoczesne prowadzenie wielu równoległych turniejów


• (na przykład 10), z których każdy obejmuje do 64 graczy i kilkuset kibiców.
Do korzystania z systemu musi być wystarczająca łączność za pośrednictwem
m o d e m u analogowego.

Wspieralność o Operator musi mieć możliwość rejestrowania nowych gier i definiowania


n o w y c h stylów rozgrywek. Czynności te mogą wymagać chwilowego
zamknięcia systemu i dodania do niego nowych modułów (na przykład klas
w języku Java), nie mogą jednak powodować konieczności jego modyfikowania.
Implementacja • Użytkownicy powinni mieć dostęp do systemu za pomocą przeglądarki W W W ,
akceptującej cookies, JavaScript i aplety Javy. Funkcje administracyjne nie

powinny być jednak dostępne za pośrednictwem przeglądarki W W W .
System powinien działać niezawodnie na dowolnej platformie uniksowej
(na przykład MacOS X, Linux czy Solaris).

Operatywność • Reklamodawca może zadłużyć się tylko do ustalonego limitu, uzgodnionego


z operatorem podczas rejestracji.

Prawo o Komunikacja z reklamodawcami — przyjmowanie ofert i odpowiedzi


na oferty — powinna się odbywać w oparciu o bezpieczne uwierzytelnianie,

stanowi bowiem podstawę do naliczania opłat.
Reklamodawcy powinni mieć możliwość wycofania się z zawartej umowy
sponsoringowej, w okresie w y z n a c z o n y m przez lokalne uregulowania
prawne w stosunku do zdalnych transakcji.

• Z b i e r a n i e w y m a g a ń o d b y w a się p r z y z n a c z ą c y m z a a n g a ż o w a n i u k l i e n t a .

• P r o g r a m i ś c i n i e p o w i n n i n i g d y z a k ł a d a ć , że d o k ł a d n i e z n a j ą p o t r z e b y k l i e n t a .

• Zbieranie wymagań pozafunkcyjnych wymaga d o k u m e n t o w a n i a i rozstrzygania


k o n f l i k t ó w ze s t r o n y w s z y s t k i c h u c z e s t n i k ó w .

4.7. Literatura uzupełniająca


K o n c e p c j a p r z y p a d k ó w użycia z o s t a ł a s p o p u l a r y z o w a n a p r z e z I. J a c o b s o n a w j e g o s ł y n n e j
książce [Jacobson i in., 1992]. Z kolei książka J. M . Carolla [Carroll, 1995], s t a n o w i ą c a rezultat
wczesnych b a d a ń n a d w y m a g a n i a m i o p a r t y m i n a scenariuszach i, ogólnie, n a koncepcji projektu
206 Rozdział 4. • Zbieranie wymagań

uczestników, zawiera wiele publikacji czołowych badaczy tematyki scenariuszy i przypadków


użycia. W książce tej opisano jednocześnie ograniczenia i pułapki tej metodologii, aktualne po
dziś dzień.
Spośród przewodników poświęconych konkretnym metodologiom, książka L. L. Con-
stantine'a i L. A. D. Lockwooda [Constantine i Lockwood, 1999] zawiera dużo materiału na te-
mat systemów opisywanych za pomocą przypadków użycia, ze szczególnym uwzględnieniem
pozyskiwania mało precyzyjnej wiedzy od użytkowników i klientów — temat ten pomijany
jest zwykle milczeniem w podręcznikach z zakresu inżynierii oprogramowania. W książce
A. Cockburna [Cockburn, 2001] i na towarzyszącej jej stronie http://www.usecases.org) znaleźć
można wiele praktycznych heurystyk tworzenia przypadków użycia w postaci tekstowej (w od-
różnieniu od ich rysowania).
W procesie zbierania wymagań użytkownicy pełnią rolę krytyczną. D. A. Norman w swej
książce [Norman, 2002] ilustruje tę tezę na podstawie zwyczajnych obiektów spotykanych
w życiu codziennym — drzwi, pieców i kurków przy kranach. Jest on rzecznikiem tezy, że
nie można wymagać od ludzi czytania podręczników ani uczenia się nowych technologii
w związku z każdą rzeczą, jaką przychodzi im używać: zamiast tego, odpowiednia wiedza na
temat produktu — jak ta, w którą stronę otwiera się drzwi — powinna być organicznie wbu-
dowana w projekt tego produktu. Norman posługuje się powszechnymi codziennymi obiektami,
lecz tę samą zasadę należy zastosować do systemów komputerowych i interfejsów użyt-
kownika.
Literatura z zakresu inżynierii wymagań okazuje się — niestety — wyjątkowo uboga, gdy
chodzi o wymagania pozałunkcyjne. Środowisko NFR, opisane w pracy L. Chunga, B. A. Nixona,
E. Yu i J. Mylopoulosa [Chung i in., 1999], jest jedną z niewielu systematycznych metod tej
inżynierii.
Szablon dokumentu RAD, cytowany w tym rozdziale, jest tylko jednym z przykładów
organizowania dokumentów poświęconych wymaganiom. W publikacji [IEEE Std. 830-1998]
opisany jest standard specyfikacji wymagań dla systemu informatycznego, zaś w załączniku
znaleźć można kilka prostych szkiców opisu konkretnych wymagań.
Prezentowane w tym rozdziale przykłady stanowią wyraz dialektycznego podejścia do
zbierania wymagań jako procesu dyskusji i negocjacji między programistami, klientem i użyt-
kownikami. Filozofia ta sprawdza się znakomicie, gdy klient jest jednocześnie użytkownikiem
lub ma dostatecznie szczegółową wiedzę z zakresu dziedziny aplikacyjnej. W ogromnych sys-
temach, takich jak system kontroli ruchu lotniczego, żaden z użytkowników ani nikt z przed-
stawicieli klienta nie ma jednak pełnego wyobrażenia o systemie. W tej sytuacji metoda dia-
lektyczna zawodzi, bowiem wiele niejawnych aspektów zachowania użytkowników wychodzi
na jaw zbyt późno. W ostatnim dziesięcioleciu w sukurs inżynierii wymagań nieoczekiwanie
przyszła gałąź antropologii — etnografia. Zgodnie z tym podejściem, analitycy wtapiają się
w społeczność użytkowników, obserwują uważnie ich codzienną prace, uczestniczą w ich ze-
braniach i tak dalej, zapisując swe obserwacje bez emocjonalnego zaangażowania. Celem tej
metody ma być zdobycie wiedzy, którą nawet użytkownicy rzadko sobie uświadamiają. Metoda
spójności, opisana w pracy S. Villera i I. Sommerville'a [Viller i Sommerville, 1999], jest
praktycznym przykładem zastosowania etnografii w zbieraniu wymagań.
Zarządzanie identyfikowalnością wymagań jest wciąż przedmiotem badań; zaintereso-
wanych czytelników odsyłamy do specjalistycznej pracy M. Jarkego [Jarkę, 1998],
4.8. Ćwiczenia 207

Na koniec polecamy książkę M. Jacksona [Jackson, 1995] — zwięzłą, wciągającą, pisaną


ciętym językiem lekturę zawierającą wiele interesujących refleksji z zakresu zasad i metod
inżynierii wymagań.

4.8. Ćwiczenia
4.1. Potraktuj swój zegarek jako system i skoryguj wyświedany czas o 2 minuty w przód.
Opisz każdą swoją interakcję z zegarkiem w postaci scenariusza. Uwzględnij wszystkie
interakcje, również te w postaci informacji zwrotnej przekazywanej przez zegarek.
4.2. W scenariuszu stworzonym w zadaniu 4.1 zidentyfikuj aktorów. Następnie utwórz
odpowiedni przypadek użycia SetTime. Uwzględnij wszystkie możliwe sytuacje: ko-
rygowanie czasu w przód i w tył, korygowanie godzin, minut i sekund.
4.3. Załóżmy, że zegarek, o którym mowa w ćwiczeniach 4.1 i 4.2, wyposażony jest w bu-
dzik. Opisz ustawianie alarmu jako niezależny przypadek użycia SetAl armTime.
4.4. Przeanalizuj dokładnie przypadki użycia SetTime i SetAl armTime z ćwiczeń 4.2 i 4.3.
Wyeliminuj z nich ewentualną redundancję za pomocą relacji zawierania. Wyjaśnij,
dlaczego relacja zawierania jest w tym przypadku bardziej odpowiednia niż relacja
rozszerzania.
4.5. Załóżmy, że Fi el dOf f i cer ma do dyspozycji system pomocy online, który dostarcza
szczegółowy opis każdego pola oraz wykaz pól obowiązkowo wypełnianych na for-
mularzu raportu o wypadku (EmergencyReport). Stwórz przypadek użycia Hel pReport
^-Emergency reprezentujący tę funkcjonalność, a następnie zmodyfikuj odpowiednio
przypadek użycia ReportEmergency (opisany w tabeli 4.5). Jakiej relacji użyjesz do
powiązania obu przypadków?
4.6. Które z poniższych wymagań pozafunkcyjnych są weryfikowalne, a które nie?
• „System musi być użyteczny",
o „System musi w sposób widoczny zareagować na akcję użytkownika w ciągu jednej
sekundy od wydania polecenia",
« „Dostępność systemu musi kształtować się na poziomie ponad 95%",
® „Interfejs użytkownika w nowym systemie musi być na tyle podobny do interfejsu
starego systemu, by użytkownik znający stary system mógł łatwo nauczyć się
obsługi nowego".
4.7. Potrzeba stworzenia kompletnej specyfikacji może zachęcać analityków do budowania
obszernych i szczegółowych dokumentów. Które z cech wymienionych w tabeli 4.1
mogą skłaniać analityków do tworzenia krótszych specyfikacji?
4.8. Utrzymywanie identyfikowalności w czasie zbierania wymagań i następnych ak-
tywności jest kosztowną procedurą, bowiem konieczne jest zbieranie i przetwarzanie
dodatkowych informacji. Jakie korzyści płynące z identyfikowalności usprawiedli-
wiają ten dodatkowy nakład pracy? Które z tych korzyści są bezpośrednio widoczne
dla analityka?
208 Rozdział 4. • Zbieranie wymagań

4.9. Wyjaśnij, dlaczego kwestionariusze z przeważającą ilością pól wielokrotnego wyboru


nie są efektywne jako podstawowy środek pozyskiwania informacji od użytkowników
w procesie zbierania wymagań.
4.10. Bazując na swoim subiektywnym odczuciu, opisz słabe i mocne strony użytkowni-
ków z perspektywy aktywności zbierania wymagań. W podobny sposób opisz słabe
i mocne strony programistów w tym procesie.
4.11. Zdefiniuj krótko pojęcie „menu". Porównaj swoją definicję z definicjami wyarty-
kułowanymi przez czterech wybranych studentów. Wyjaśnij, skąd biorą się istotne
różnice między wszystkimi pięcioma definicjami.
4.12. Stwórz wysokopoziomowy przypadek użycia ManageAdverti sement inicjowany
przez aktora Adverti ser, następnie poddaj go uszczegółowieniu, tworząc serię ko-
lejnych przypadków. Uwzględnij te cechy systemu, które umożliwiają reklamodawcy
rejestrowanie w systemie własnych banerów, wiązanie z nimi słów kluczowych, sub-
skrybowanie informacji o wybranych turniejach i ligach oraz monitorowanie stanu
obciążeń swojego konta. Upewnij się, że Twoje przypadki użycia są spójne z dekla-
racją problemu systemu ARENA.
4.13. W związku z przypadkiem użycia AnnounceTournament, przedstawionym w ta-
beli 4.13, opisz przepływ zdarzeń oraz warunki wstępne i końcowe przypadku
użycia ApplyForTournament, inicjowanego przez gracza (Player) zamierzającego
uczestniczyć w nowo zorganizowanym turnieju. Nie zapomnij o deklaracji proble-
mu. Napisz listę pytań do klienta w związku z każdą napotkaną alternatywą.
4.14. Opisz przepływ zdarzeń oraz warunki wstępne i końcowe dla „wyjątkowych" przy-
padków użycia rozszerzających przypadek AnnounceTournament, wymienionych na
rysunku 4.11. Tam, gdzie to możliwe, użyj relacji zawierania, by wyeliminować
redundancję.

Bibliografia
[Bruegge i in., 1994] B. Bruegge, K. O'Toole, D. Rothenberger „Design considerations
for an accident management system," w: M. Brodie, M. Jarke,
M. Papazoglou (red.), Proceedings of the Second International
Conference on Cooperative Information Systems, str. 90 - 100,
University of Toronto Press, Toronto, Canada, m a j 1994.
[Carroll, 1995] J. M. Carroll (red.) Scenario-Based Design: Envisioning Work
and Technology in System Development, Wiley, New York, 1995.
[Chung i in., 1999] L. Chung, B. A. Nixon, E. Yu, J. Mylopoulos Non-Functional
Requirements in Software Engineering, Kluwer Academic, Boston, 1999.
[Cockburn, 2001] A. Cockburn Writing Effective Use Cases, Addison-Wesley, Reading,
MA, 2001.
[Constantine i Lockwood, 1999] L. L. Constantine, L. A. D. Lockwood Software for Use, Addison-Wesley,
Reading, MA, 1999.
[Grady, 1992] R. Grady Practical Software Metrics for Project Management and
Process Improvement, Prentice Hall, Englewood Cliffs, NJ, 1992.
Bibliografia 209

[Hammer i Champy, 1993] M. Hammer; J. Champy Reengineering The Corporation: a Manifesto


For Business Revolution, Harper Business, New York, 1993.
[IEEE Std. 610.12-1990] IEEE IEEE Standard Computer Dictionary: A Compilation of IEEE
Standard Computer Glossaries, NY, 1990.
[IEEE Std. 830-1998] IEEE Standard for Software Requirements Specification, IEEE
Standards Board, 1998.
[ISO Std. 9126] International Standards Organization. Software engineering — Product
quality. ISO/IEC-9126, Geneva, Switzerland, 2001.
[Jackson, 1995] M. Jackson Software Requirements & Specifications: A Lexicon of Practice,
Principles and Prejudices, Addison-Wesley, Reading, MA, 1995.
[Jacobson i in., 1992] I. Jacobson, M. Christerson, P. Jonsson, G. Overgaard Object-Oriented
Software Engineering — A Use Case Driven Approach, Addison-Wesley,
Reading, MA, 1992.
[Jacobson i in., 1999] I. Jacobson, G. Booch, J. Rumbaugh The Unified Software Development
Process, Addison-Wesley, Reading, MA, 1999.
[Jarkę, 1998] M. Jarke Requirements tracing, „Communications of the ACM", t. 41,
nr 12, grudzień 1998.
[Neumann, 1995] P. G. N e u m a n n Computer-Related Risks, Addison-Wesley,
Reading, MA, 1995.
[Nielsen, 1993] J. Nielsen Usability Engineering, Academic, New York, 1993.
[Norman, 2002] D. A. N o r m a n The Design of Everyday Things, Basic Books,
New York, 2002.
[Rational] Rationale, http://www.rational.com.
J. Rumbaugh, M. Błaha, W. Premerlani, F. Eddy, W. Lorensen Object-
[Rumbaugh i in., 1991]
-Oriented Modeling and Design, Prentice Hall, Englewood Cliffs, NJ, 1991.
Telelogic, http://www.telelogic.se.
[Telelogic]
S. Viller, I. Sommerville „Social analysis in the requirements engineering
[Viller i Sommerville, 1999]
process: from ethnography to method", International Symposium on
Requirements Engineering (ISRE'99), Limerick, Ireland, czerwiec 1999.
[Wirfs-Brock i in., 1990] R. Wirfs-Brock, B. Wilkerson, L. Wiener Designing Object-Oriented
Software, Prentice Hall, Englewood Cliffs, NJ, 1990.
[Wood i Silver, 1989] J. Wood, D. Silver Joint Application Design, Wiley, New York, 1989.
5.1. Wstęp: złudzenie optyczne 212
5.2. O analizie wymagań ogólnie 212
5.3. Koncepcje analizy wymagań 214
5.3.1. Analityczny model obiektowy i modele dynamiczne 214
5.3.2. Obiekty encji, obiekty brzegowe i obiekty sterujące 215
5.3.3. Generalizacja i specjalizacja 216
5.4. Aktywności analizy wymagań: od przypadków użycia do obiektów 217
5.4.1. Identyfikacja obiektów encji 218
5.4.2. Identyfikacja obiektów brzegowych 220
5.4.3. Identyfikacja obiektów sterujących 222
5.4.4. Odwzorowywanie przypadków użycia w obiekty
za pomocą diagramów sekwencji 224
5.4.5. Modelowanie interakcji między obiektami
za pomocą kart CRC 228
5.4.6. Identyfikacja skojarzeń 228
5.4.7. Identyfikacja agregacji 231
5.4.8. Identyfikacja atrybutów 232
5.4.9. Modelowanie zachowania poszczególnych obiektów
uzależnionego od ich stanu 233
5.4.10. Modelowanie relacji dziedziczenia między obiektami 234
5.4.11. Przeglądy modelu analitycznego 235
5.4.12. Podsumowanie analizy 236
5.5. Zarządzanie analizą wymagań 237
5.5.1. Dokumentowanie analizy wymagań 238
5.5.2. Przydzielanie odpowiedzialności 239
5.5.3. Komunikacja w związku z analizą wymagań 240
5.5.4. Iteracje modelu analitycznego 241
5.5.5. Uzgodnienie modelu analitycznego z klientem 243
5.6. Analiza przypadku — system ARENA 245
5.6.1. Identyfikacja obiektów encji 245
5.6.2. Identyfikacja obiektów brzegowych 250
5.6.3. Identyfikacja obiektów sterujących 251
5.6.4. Modelowanie interakcji między obiektami 252
5.6.5. Weryfikacja i konsolidacja modelu analitycznego 254
5.6.6. Wnioski 256
5.7. Literatura uzupełniająca 258
5.8. Ćwiczenia 258
Bibliografia 260
Analiza wymagań

Mam na imię Foo, na nazwisko...


gdybym tylko mógł sobie przypomnieć...
— programista o bardzo małym rozumku

ezułtatem analizy jest model wymagań, w intencji twórców poprawny, kompletny,


spójny i jednoznaczny. Programiści formalizują specyfikację wymagań, która powstała
podczas ich zbierania, i bardziej szczegółowo przyglądają się warunkom granicznym oraz
sytuacjom wyjątkowym. Weryfikują wymagania i korygują, gdy znajdą błędy lub niejedno-
znaczności. Wszelkie zmiany i uzupełnienia odbywają się zwykle w porozumieniu z klientem,
który ewentualnie dostarcza dodatkowych informacji.
W trakcie analizy zorientowanej obiektowo programiści budują model opisujący dzie-
dzinę aplikacyjną. Przykładowo model analityczny zegarka szczegółowo opisuje sposób wy-
świetlania przezeń czasu i daty: czy pomiar czasu ma uwzględniać lata przestępne? Czy ma
być wyświetlany dzień tygodnia? Fazy Księżyca? Następnie model analityczny rozszerzany
jest o opis interakcji między aktorami a systemem, polegających na manipulowaniu obiektami
dziedziny aplikacyjnej: w jaki sposób użytkownik zegarka resetuje wskazanie czasu? Jak może
resetować wskazanie dnia tygodnia? 1 Programiści wykorzystują model analityczny, wraz
z wymaganiami pozafunkcyjnymi, do przygotowania architektury systemu, opracowywanej
na etapie projektu wysokiego poziomu (patrz rozdział 6. „Projektowanie systemu — de-
kompozycja na podsystemy"). W tym rozdziale omówimy szczegółowo aktywności związane
z analizą wymagań. Skupimy się na identyfikacji obiektów, ich zachowaniu, klasyfikacji i or-
ganizacji. Opiszemy także menedżerskie aspekty związane z analizą w kontekście wieloze-
społowej realizacji projektu. Na zakończenie zaprezentujemy zastosowanie omówionych
koncepcji na konkretnym przykładzie, jakim jest system ARENA, poznany w poprzednim
rozdziale.

' Oczywiście, dzień tygodnia da się obliczyć na podstawie aktualnej daty za pomocą prostego algorytmu
(jeden z licznych przykładów: http://www.mimuw.edu.pl/delta/artykuly/delta2010-03/2010-03-tydzien.pdf),
który — włączony do oprogramowania zegarka — całkowicie oddziela użytkownika od bezpośredniego
manipulowania tą częścią wyświetlanej daty. Teoretycznie można sobie jednak wyobrazić prymitywny
zegarek, w którym początkowe ustawienie, dokonane przez użytkownika, inkrementowane jest (cy-
klicznie) z upływem każdej doby — przyp. tłum.
212 Rozdział 4. •Zbieraniewymagań

5.1. Wstęp: złudzenie optyczne


W 1915 roku duński psycholog E. Rubin opublikował słynną rycinę ilustrującą koncepcję
percepcji dwustabilnej. Co widzimy na rysunku 5.1: dwie spoglądające na siebie twarze czy biały
wazon na czarnym tle? Zależnie od własnej interpretacji możemy widzieć raz jedno, raz drugie.

Rysunek 5.1. Przykład niejednoznaczności — co przedstawia ten rysunek?

A gdyby rysunek 5.1 był specyfikacją wymagań, jaki model — wazonu czy konwersacji tete
a tete — należałoby zbudować na jej podstawie? Typowa specyfikacja wymagań początkowo ma
w sobie coś na kształt takiego właśnie rysunku, co jest konsekwencją zarówno nieprecyzyjnej
natury języka naturalnego (w nim formułowane są wymagania), jak i ukrytych założeń, niejawnie
(i czasami nieświadomie) przyjmowanych przez autorów tejże specyfikacji. Kilka tego przykła-
dów opisaliśmy już na początku poprzedniego rozdziału, sami też często popełniamy błędy tego
rodzaju, na przykład podając znajomym z odległego kraju nasz numer telefonu bez prefiksu +48.
Proces formalizowania wymagań ma na celu wykrycie takich właśnie przeoczeń, wielo-
znaczności i innych uchybień w specyfikacjach, które to uchybienia rozstrzygane są następnie
w drodze konsultacji z klientem dostarczającym dodatkowych informacji. W taki oto sposób
aktywności zbierania wymagań i ich analizowania sukcesywnie przeplatają się ze sobą.

5.2. O analizie wymagań ogólnie


Celem analizy jest stworzenie modelu systemu, zwanego modelem analitycznym — modelu
poprawnego, kompletnego, spójnego i weryfikowalnego. Analiza wymagań tym różni się od
ich zbierania, że wysiłek programistów skupia się na strukturalizowaniu i formalizowaniu tych
wymagań (patrz rysunek 5.2), co prowadzi do spojrzenia na ich zestaw z nowej perspektywy
i ułatwia zauważenie zawartych w tym zestawie błędów. Ponieważ model analityczny może
nie być dostatecznie zrozumiały dla klienta i użytkowników, na programistach spoczywa
5.2. O analizie wymagań ogólnie 213

Rysunek 5.2. Produkty aktywności zbierania i analizowania wymagań

odpowiedzialność za uaktualnianie specyfikacji wymagań zgodnie z wnioskami wyciągnię-


tymi w trakcie ich analizy, bo dopiero uaktualniona specyfikacja przedstawiana jest klientowi
i użytkownikom do weryfikacji.
Tak to już w życiu bywa (także w życiu użytkowników i programistów), że trudne decyzje
zwykło się odkładać „na potem". Trudności w podejmowaniu decyzji mogą wynikać z niedo-
statecznej wiedzy z zakresu dziedziny aplikacyjnej, niedostatecznej znajomości poszczególnych
technologii lub ze zwykłych nieporozumień między programistami a użytkownikami. Uni-
kanie konfrontacji z realiami przyczynia się, owszem, do szybszego postępu w realizacji pro-
jektu — ale tylko do momentu, gdy rzeczone decyzje muszą zostać podjęte, i to znacznie wyż-
szym kosztem niż pierwotnie, bowiem skutki niefrasobliwości dają o sobie znać na etapie
testowania lub, co gorsza, już w trakcie eksploatacji systemu. Tłumaczenie specyfikacji wy-
magań na formalny lub półformalny model zmusza wręcz programistów do rozwiązywania
wielu problemów jak najwcześniej.
Na model analityczny składają się trzy modele szczegółowe: model funkcjonalny re-
prezentowany przez przypadki użycia i scenariusze, analityczny model obiektowy w postaci
diagramów klas i obiektów oraz model dynamiczny odzwierciedlany jako zbiór diagramów
stanów i diagramów sekwencji (patrz rysunek 5.3).
W poprzednim rozdziale opisywaliśmy zbieranie wymagań od użytkowników i opisy-
wanie ich w postaci przypadków użycia i scenariuszy; w tym rozdziale pokażemy, jak ulepszać
model funkcjonalny i na jego podstawie budować modele obiektowy i dynamiczny — napi-
szemy, w jaki sposób prowadzi to ku bardziej precyzyjnej i kompletnej specyfikacji, w miarę
dodawania nowych szczegółów do modelu analitycznego. Rozdział zakończymy opisem ak-
tywności menedżera, związanych z analizą wymagań.
Rozpoczniemy od zdefiniowania podstawowych koncepcji.
214 Rozdział 4. •Zbieraniewymagań

Rysunek 5.3. Model analityczny jako złożenie modelu funkcjonalnego, analitycznego modelu obiekto-
wego i modelu dynamicznego. W języku UML model funkcjonalny reprezentowany jest w postaci
diagramów przypadków użycia, model obiektowy — w postaci diagramów klas, a model dynamiczny
— w postaci diagramów sekwencji i diagramów stanów

5.3. Koncepcje analizy wymagań


W tej sekcji opiszemy podstawowe koncepcje i pojęcia analizy, wykorzystywane w niniejszym
rozdziale, czyli:

• Analityczny model obiektowy i modele dynamiczne (patrz sekcja 5.3.1),


• Obiekty encji, obiekty brzegowe i obiekty sterujące (patrz sekcja 5.3.2),
• Generalizację i specjalizację (patrz sekcja 5.3.3).

5.3.1. Analityczny model obiektowy i modele dynamiczne


Model analityczny reprezentuje tworzony system z perspektywy jego użytkownika. Anali-
tyczny model obiektowy, jako część modelu analitycznego, odzwierciedla indywidualne kon-
cepcje korzystania z systemu, ich właściwości i relacje między nimi. Model ten reprezentowany
jest w formie diagramu klas, z uwzględnieniem ich atrybutów i operacji. Stanowi wizualny
słownik podstawowych koncepcji użytkownika.
Model dynamiczny koncentruje się na zachowaniu systemu. Opisywany jest przez dia-
gramy sekwencji i diagramy stanów. Diagramy sekwencji reprezentują interakcje między obiek-
tami uczestniczącymi w pojedynczym przypadku użycia, zaś każdy diagram stanów opisuje
zachowanie pojedynczego obiektu (lub grupy obiektów ściśle ze sobą powiązanych). W ramach
modelu dynamicznego widoczne staje się przyporządkowanie zakresów odpowiedzialności
poszczególnym klasom, a zwykle także identyfikowanie nowych klas, skojarzeń i atrybutów,
o jakie należy wzbogacić analityczny model obiektowy.
Należy pamiętać, że zarówno analityczny model obiektowy, jak i model dynamiczny re-
prezentują koncepcje użytkownika, a nie klasy czy komponenty języka programowania. Klasy
w rodzaju Database (baza danych), Subsystem (podsystem), SessionManager (menedżer sesji)
5.3. Koncepcje analizy wymagań 215

czy Network (sieć) nie powinny w ogóle pojawić się w modelu analitycznym z tego prostego
względu, że użytkownikowi mogą być zupełnie obce koncepcje reprezentowane przez te klasy.
Notabene nie zmienia to w niczym faktu, że większość klas występujących w modelu anali-
tycznym ma swe odzwierciedlenie w postaci klas języka programowania używanego do imple-
mentacji systemu, jednak klasy tej drugiej grupy posiadają zazwyczaj więcej atrybutów i uwi-
kłane są w bogatszy układ interakcji niż ich pierwowzory. W konsekwencji klasy modelu
analitycznego mogą być uważane za wysokopoziomowe abstrakcje, których ukonkretnienie
odbywać się będzie w dalszych stadiach projektu.
Na rysunku 5.4 widzimy przykład dobrego i złego wyboru klas dla modelu analitycznego
reprezentującego przykładowy zegarek SatWatch.

Rysunek 5.4. Przykłady i kontrprzykłady wyboru klas dla modelu analitycznego zegarka SatWatch

5.3.2. Obiekty encji, obiekty brzegowe i obiekty sterujące


I. Jacobson, G. Booch, J. Rumbaugh w swojej książce dzielą obiekty tworzące analityczny model
obiektowy na obiekty encji, obiekty brzegowe i obiekty sterujące [Jacobson i in., 1999]. Obiekty
encji reprezentują trwałą informację przetwarzaną przez system. Obiekty brzegowe odzwier-
ciedlają interakcje między aktorami a systemem. Za realizację przypadków użycia odpowie-
dzialne są obiekty sterujące. W przypadku zegarka 2Bwatch, opisywanego w rozdziale 2.,
obiekty Year, Month i Day są obiektami encji, Button i LCDDi spl ay to obiekty brzegowe, zaś
obiektem sterującym jest ChangeDateControl reprezentujący aktywność zmieniania daty,
rozpoczynającą się od naciśnięcia odpowiedniej kombinacji przycisków. Podział obiektów
modelu na trzy wymienione kategorie dostarcza programistom prostą heurystykę pomagającą
w rozróżnieniu odmiennych, choć powiązanych ze sobą koncepcji — i tak na przykład czas
odmierzany za pomocą zegarka ma zupełnie inne właściwości niż wyświetlacz służący do jego
pokazywania. Różnicę tę uwydatnia odróżnienie obiektów brzegowych od obiektów encji:
czas jako taki reprezentowany jest przez obiekt encji Time, zaś reprezentantem wyświetlacza
jest obiekt brzegowy LCDDi spl ay. Rozdział między wspomnianymi trzema kategoriami spra-
wia ponadto, że projekt staje się bardziej elastyczny: interfejs systemu (odzwierciedlany przez
obiekty brzegowe) jest bardziej podatny na zmiany niż podstawowa funkcjonalność tego sys-
temu (reprezentowana przez obiekty encji), zatem oddzielając jedno od drugiego, powodujemy,
że jedna część modelu staje się niewrażliwa na zmiany dokonywane w ramach drugiej.
216 Rozdział 4. •Zbieraniewymagań

Podczas rozróżniania różnych typów obiektów modelu analitycznego wykorzystywany


jest mechanizm stereotypów UML, za pomocą którego programiści dołączają metainformację
do modelowanych elementów. Przykład tego widzimy na rysunku 5.5, gdzie obiekt Change
^•DateControl opatrzony został etykietą stereotypu «control». Dodatkowo we wspomnianym
rozróżnieniu dopomóc mogą odpowiednie konwencje nazewnicze: obiektom sterującym
można w tym celu nadawać nazwy kończące się przyrostkiem Control, obiektom brzegowym
— nazwy kończące się przyrostkiem odzwierciedlającym ich naturę, na przykład Form (for-
mularz), Button (przycisk), Di spl ay (wyświetlacz) czy po prostu Boundary; obiekty encji nie
wymagają w tym kontekście żadnego specjalnego wyróżniania swoich nazw. Opisana kon-
wencja okazuje się pożyteczna również tam, gdzie nie istnieje odpowiednik stereotypów UML,
na przykład w kodzie źródłowym programu.

Rysunek 5.5. Klasy analitycznego modelu obiektowego zegarka 2BWatch. Stereotypy « e n t i t y » ,


"Control» i «boundary» odpowiadają obiektom (kolejno) encji, sterującym i brzegowym

5.3.3. Generalizacja i specjalizacja


Jak już pisaliśmy w rozdziale 2. „Modelowanie w języku UML", relacja dziedziczenia umożli-
wia hierarchiczne organizowanie koncepcji. Na szczycie („korzeniu") takiej hierarchii znajduje
się koncepcja najbardziej ogólna, taka jak Inci dent (wypadek) na rysunku 5.6, na samym dole
(w „liściach") koncepcje najbardziej szczegółowe, stanowiące wynik ostatecznej konkretyzacji:
CatlnTree (kot na drzewie), TrafficAcccident (wypadek drogowy), BuildingFire (pożar
budynku), EarthQuake (trzęsienie ziemi) czy Chemical Leak (skażenie chemiczne). Węzły po-
średnie w drzewie hierarchii stanowią wynik mniej lub bardziej zaawansowanej kon-
kretyzacji — LowPriori ty Inci dent (wypadek niewymagający natychmiastowej interwencji),
Emergency (niebezpieczeństwo) i Disaster (kataklizm). Dzięki zróżnicowaniu stopnia kon-
kretyzacji, możemy odwoływać się do określonych koncepcji w sposób precyzyjny: używając
słowa „wypadek" (lub klasy Inci dent), mamy na myśli wszelkie rodzaje wypadków, zaś słowo
„niebezpieczeństwo" (i klasa Emergency) odnosi się tylko do określonej ich kategorii (wy-
padków wymagających pilnej interwencji).
Generalizowaniem nazywamy w modelowaniu aktywność identyfikowania abstrak-
cyjnych koncepcji na podstawie przykładów ich konkretyzacji. Załóżmy na przykład, że pró-
bujemy opisywać działanie istniejącego systemu zarządzania wypadkami: właśnie przeanali-
zowaliśmy dialog i ekrany związane z wypadkiem drogowym, potem dialog i ekrany związane
z pożarem, a teraz na podstawie elementów wspólnych dla obu kategorii zdarzeń definiujemy
abstrakcyjną kategorię „niebezpieczeństwo", której wypadek drogowy i pożar są konkretyzacjami.
5.4. Aktywności analizy wymagań: od przypadków użycia do obiektów 217

Rysunek 5.6. Przykład generalizowania koncepcji: w korzeniu drzewa reprezentowana jest koncep-
cja najbardziej ogólna, w jego liściach koncepcje najbardziej szczegółowe

Specjalizowanie to aktywność odwrotna, czyli identyfikowanie koncepcji bardziej spe-


cyficznych na podstawie koncepcji wysokopoziomowej. Załóżmy mianowicie, że tworzymy
„od zera" system zarządzania wypadkami i właśnie dyskutujemy z klientem elementy funk-
cjonalne tego systemu. Klient najpierw wprowadza nas w ogólną koncepcję wypadku (Inci dent),
po czym wyróżnia trzy kategorie wypadków: kataklizm (Di saster) wymagający współdziałania
wielu agencji, niebezpieczeństwo (Emergency) jako wypadek wymagający pilnej interwencji,
lecz ze strony pojedynczej agencji, oraz wypadek mało istotny (LowPri ori t y l n c i dents),
w sprawie którego interwencję można odłożyć na później, jeżeli zasoby potrzebne są aktualnie
do obsługi wypadków bardziej pilnych.
W obu przypadkach — generalizowania i specjalizowania — mamy do czynienia z relacją
dziedziczenia między koncepcjami, dlatego programiści często nazywają relacje dziedziczenia
relacjami „ogół-szczegóły". W tej książce używać będziemy terminu „dziedziczenie" na
określenie relacji między klasami, natomiast określenia „generalizowanie" i „specjalizowanie"
(lub krótko: „generalizacja" i „specjalizacja") stosować będziemy na oznaczenie aktywności
modelowania wynikających z relacji dziedziczenia.

5.4. Aktywności analizy wymagań:


od przypadków użycia do obiektów
W tej sekcji opiszemy aktywności związane z transformowaniem przypadków użycia i scena-
riuszy (utworzonych w trakcie zbierania wymagań) w model analityczny. Wspomniane ak-
tywności obejmują:

• Identyfikację obiektów encji (patrz sekcja 5.4.1),


® Identyfikację obiektów brzegowych (patrz sekcja 5.4.2),
• Identyfikację obiektów sterujących (patrz sekcja 5.4.3),
» Odwzorowywanie przypadków użycia w obiekty za pomocą diagramów sekwencji
(patrz sekcja 5.4.4),
218 Rozdział 4. •Zbieraniewymagań

• Modelowanie interakcji między obiektami za pomocą kart CRC (patrz sekcja 5.4.5),
• Identyfikację skojarzeń (patrz sekcja 5.4.6),
• Identyfikację agregacji (patrz sekcja 5.4.7),
• Identyfikację atrybutów (patrz sekcja 5.4.8),
• Modelowanie zachowania poszczególnych obiektów uzależnionego od ich stanu
(patrz sekcja 5.4.9),
• Modelowanie relacji dziedziczenia między obiektami (patrz sekcja 5.4.10),
• Przeglądy modelu analitycznego (patrz sekcja 5.4.11).

Każdą z wymienionych aktywności zilustrujemy przykładem przypadku użycia Report


^•Emergency systemu FRIEND opisywanego w rozdziale 4. „Zbieranie wymagań" . Aktywności
te sterowane są w większości przez heurystyki, a jakość powstających w ich wyniku produktów
uzależniona jest w dużej mierze od doświadczenia programisty stosującego owe heurystyki. Pre-
zentowane w tej sekcji metody i heurystyki zaczerpnęliśmy z pracy T. De Marco [De Marco,
1978] i prac zbiorowych [Jacobson i in., 1999], [Rumbaugh i in., 1991] i [Wirfs-Brock i in., 1990].

5.4.1. Identyfikacja obiektów encji


Obiekty uczestniczące w przypadku użycia (patrz sekcja 4.4.6) formują podstawę modelu
analitycznego. Obiekty te, jak pisaliśmy w rozdziale 4. „Zbieranie wymagań", odnajduje się
drogą uważnej analizy każdego przypadku użycia. Gdy przypadek ten opisywany jest w języ-
ku naturalnym, kilka prostych heurystyk, zwanych heurystykami Abbotta od nazwiska ich
autora [Abbott, 1983], w sposób intuicyjny może dopomóc w zidentyfikowaniu obiektów, atry-
butów i skojarzeń między nimi na podstawie specyfikacji wymagań. Heurystyki Abbotta do-
konują wprost odwzorowania części mowy języka angielskiego (rzeczowników, czasowników,
przymiotników) na potencjalne komponenty modelu (obiekty, operacje, relacje dziedziczenia
i klasy). W tabeli 5.1 prezentujemy przykład takiego odwzorowania dla przypadku użycia
ReportEmergency, przedstawionego w tabeli 5.2.

Tabela 5.1. Heurystyki Abbotta, odwzorowujące części mowy w modele k o m p o n e n t u

Część mowy lub zdania Komponent modelu Przykład z systemu FRIEND


Rzeczownik własny Obiekt Alice

Rzeczownik pospolity Klasa FieldOfficer

Czasownik — czynność Operacja c r e a t e s („tworzy"), submits („wysyła"),


s e l e c t s („wybiera")

Czasownik — bycie Dziedziczenie I s a kind of ... („jest r o d z a j e m . . . " ) ,


i s one of e i t h e r . . . („jest jednym z ...")

Czasownik — posiadanie Agregacja Has („posiada"),consists of ...


(„składa się i . . . " ) , i ncl udes ... („zawiera")
Czasownik modalny Ograniczenie Must be ... („musi być ...")
— imperatyw

Przymiotnik, przydawka Atrybut Incident d e s c r i p t i o n („opis wypadku")


5.4. Aktywności analizy wymagań: od przypadków użycia do obiektów 219

Tabela 5.2. Przykładowy przypadek użycia ReportEmergency (w wariancie jednokolumnowym)

Nazwa przypadku użycia ReportEmergency

Warunek wstępny 1. F i e l d O f f i c e r aktywuje f u n k c j ę „Report Emergency" na swym


terminalu.

Przepływ zdarzeń 2. System FRIEND wyświetla w odpowiedzi stosowny formularz.


Formularz ten zawiera pola określające między innymi rodzaj
niebezpieczeństwa (ogólne, pożar, komunikacyjne), lokalizację
zdarzenia, jego opis, żądanie zasobów i udział materiałów
niebezpiecznych.
3. Fi el dOf f i cer wypełnia f o r m u l a r z z obowiązkowymi p o l a m i
reprezentującymi typ zdarzenia i jego krótki opis. Fi el dOf f i cer
określa także możliwe sposoby pierwszej reakcji na zagrożenie
i w y m a g a n e do tej reakcji zasoby. Po wypełnieniu f o r m u l a r z a
Fi el dOf f i cer wysyła go do systemu, klikając przycisk Send report.
W rezultacie system FRIEND powiadamia dyspozytora (Di spatcher)
0 zdarzeniu.
4. Di s p a t c h e r analizuje o t r z y m a n e od Fi el dOf f i c e r a i n f o r m a c j e
zawarte w formularzu, po czym tworzy nowy obiekt Inci dent w bazie,
realizując przypadek użycia Open Inci dent. Di spatcher określa sposób
reakcji, przyporządkowuje niezbędne zasoby (zgodnie z przypadkiem
użycia Al 1 ocateResources) i zatwierdza raport, wskutek czego system
FRIEND i n f o r m u j e aktora F i e l d O f f i c e r o zatwierdzeniu r a p o r t u
1 przekazuje mu informację od dyspozytora.

Warunki końcowe 5. Fi el dOf f i cer otrzyma! potwierdzenie od dyspozytora.

Analiza opisu przypadków użycia w języku naturalnym ma tę zaletę, iż język ten odzwier-
ciedla świat widziany z perspektywy użytkownika. Ma ona jednak wiele ograniczeń, z których
najważniejsze są dwa. Po pierwsze, jakość wynikowego modelu w dużej mierze zależy od stylu
pisarskiego analityka (spójności używanej terminologii, transformacji między częściami mowy
i tym podobnych). Języka naturalny jest bardzo nieprecyzyjnym narzędziem i model tworzenie
modelu obiektowego literalnie na bazie nieprecyzyjnego opisu niesie ze sobą ryzyko, iż model
ten będzie równie nieprecyzyjny. Programiści starają się unikać tego ryzyka poprzez odpo-
wiednie przeredagowywanie specyfikacji wymagań w taki sposób, by używana w niej termi-
nologia była w jak najwyższym stopniu zestandaryzowana. Drugi mankament wynika z bo-
gactwa i redundancji samego języka naturalnego: liczba rzeczowników jest znacznie większa
od liczby rzeczy identyfikowanych przez te rzeczowniki. Na określenie danego przedmiotu,
koncepcji, zjawiska i tak dalej istnieje zwykle wiele synonimów; zidentyfikowanie, sortowanie
i utożsamianie owych synonimów w obszernej specyfikacji wymagań jest procesem niezwykle
pracochłonnym. Dlatego heurystyki Abbotta nadają się raczej do wstępnego określenia
obiektów kandydatów na rzeczywiste obiekty modelu na podstawie krótkich opisów, na przy-
kład opisów przepływu zdarzeń w scenariuszach i przypadkach użycia. W uzupełnieniu do heu-
rystyk Abbotta można polecić kilka następujących:
220 Rozdział 4. •Zbieraniewymagań

Heurystyki pomocne w identyfikowaniu obiektów encji


W ś r ó d obiektów modelu wyróżnić można między innymi:
• terminy, które programiści muszą uzgodnić z u ż y t k o w n i k a m i w celu r o z u m i e n i a
p r z y p a d k ó w użycia,
• rzeczowniki powtarzające się w przypadkach użycia ( I n c i d e n t ) ,
• encje świata rzeczywistego odzwierciedlone w systemie ( F i e l d O f f i c e r , Di spatcher, Resource),
® procesy świata rzeczywistego odzwierciedlone w systemie (EmergencyOperationsPlan),
• miejsca generowania i powstawania danych (Pri n t e r ) .

Programiści przyporządkowują obiektom nazwy i krótkie opisy, określają też ich atry-
buty i znaczenie każdego z nich. Używanie unikalnych nazw dla obiektów przyczynia się do
promowania standardowej terminologii. Dla obiektów encji zalecamy bezwzględnie wykorzy-
stywanie nazw, którymi posługują się użytkownicy i specjaliści z dziedziny aplikacyjnej; opisy
obiektów, nawet krótkie, pozwalają programistom na wyjaśnienie koncepcji reprezentowanej
przez te obiekty i unikanie nieporozumień (wynikających na przykład z używania tego samego
obiektu do reprezentowania koncepcji powiązanych ze sobą, choć całkowicie różnych). Pro-
gramiści nie powinni jednak ustalać nadmiernej szczegółowości obiektów zbyt wcześnie, gdy
proces analizy jest jeszcze tak naprawdę w stadium nieustalonym: powinni oni dokumentować
te atrybuty i koncepcje, które nie są oczywiste; dla pozostałych obiektów wystarczające okażą
się nazwy (być może tymczasowe) i treściwe opisy. Potrzebne będzie jeszcze wiele iteracji,
w ramach których zmieniać się będą zarówno funkcje realizowane przez poszczególne obiekty,
jak i prawdopodobnie nazwy tychże obiektów. Gdy jednak model analityczny stanie się już
stabilny, opis każdego obiektu powinien być tak szczegółowy, jak tylko jest to konieczne
(powrócimy do tej kwestii w sekcji 5.4.11).
I tak na przykład w przypadku użycia prezentowanym w tabeli 5.2 wykorzystaliśmy
wiedzę z zakresu dziedziny aplikacyjnej i wywiady z użytkownikami do identyfikacji klas
Dispatcher, EmergencyReport, F i e l d O f f i c e r i Incident. Zauważmy przy tym, że obiekt
klasy EmergencyReport nie jest nigdzie przywoływany explicite przez nazwę, a jedynie w formie
opisowej („otrzymane od Fi el dOf f i cera informacje zawarte w formularzu"); po konsultacjach
z klientem dowiadujemy się, że taka informacja nazywana jest potocznie „raportem o niebez-
pieczeństwie" (emergency report) i decydujemy się na reprezentowanie jej w postaci stosow-
nego obiektu, którego klasę nazywamy EmergencyReport.
Zebrana w ten sposób wiedza prowadzi do początkowej postaci modelu analitycznego,
którego obiekty encji przedstawiamy w tabeli 5.3 Zauważmy, że modelowi temu bardzo daleko
do kompletnego opisu systemu implementującego przypadek użycia ReportEmergency.
W następnej sekcji zajmiemy się obiektami brzegowymi przypadku użycia Report
^-Emergency.

5.4.2. Identyfikacja obiektów brzegowych


Obiekty brzegowe stanowią reprezentację interfejsu systemu dla współdziałających z nim akto-
rów. W każdym przypadku użycia każdy aktor przejawia interakcję z przynajmniej jednym
obiektem brzegowym. Obiekty brzegowe kolekcjonują informację otrzymywaną od aktorów
i przekształcają ją na formę wykorzystywaną przez obiekty encji i obiekty sterujące. Zbiór
Tabela 5.3. Obiekty encji uczestniczące w przypadku użycia Report Emergency
Dispatcher Dyspozytor, oficer policji zarządzający wypadkami (Inci dent). Di spatcher
dokonuje otwierania, dokumentowania i zamykania obsługi wypadków, zgodnie
z raportami (EmergencyReport) i inną informacją pochodzącą od funkcjonariusza
(Fi el dOffi cer). Dispatcher identyfikowany jest na podstawie numeru odznaki.
EmergencyReport Początkowy raport o wypadku, sporządzony przez funkcjonariusza (Fi el dOffi cer)
dla dyspozytora (Di spatcher). Raport taki zwykle powoduje utworzenie nowego
obiektu klasy Incident przez dyspozytora. Podstawowymi elementami raportu
są zwykle: stopień zagrożenia, typ wypadku (pożar, wypadek drogowy, inne
zdarzenie), wskazanie miejsca wypadku, krótki opis sytuacji.
Fi el dOffi cer Funkcjonariusz na służbie. Dany funkcjonariusz może być w danej chwili
przydzielony do co najwyżej jednego wypadku (Inci dent). Funkcjonariusze
(Fi el dOffi cer) identyfikowani są na podstawie numeru odznaki.
Incident Wypadek, sytuacja wymagająca interwencji funkcjonariusza (Fi el dOffi cer).
Wypadek może zostać zgłoszony do systemu przez funkcjonariusza lub przez
inną osobę pozostającą na zewnątrz tego systemu. Informacja o wypadku (Incident)
obejmuje jego opis, sposób reakcji, status (otwarty, zamknięty, udokumentowany),
lokalizację i identyfikację funkcjonariuszy (Fi el dOffi cer) przydzielonych do obsługi.

o b i e k t ó w b r z e g o w y c h m o d e l u m o ż e być u w a ż a n y za przybliżenie interfejsu u ż y t k o w n i k a


— „przybliżenie", b o w i e m obiekty brzegowe p o w i n n y egzystować w o d e r w a n i u od wizual-
nych aspektów tego interfejsu: opcja m e n u czy pasek przewijania nie są d o b r y m i k a n d y d a t a m i
n a obiekty brzegowe, b o w i e m n a w i ą z u j ą już d o szczegółów wizualnych w s p o m n i a n e g o
interfejsu. T e j e d n a k ustalane są w s t ę p n i e z k l i e n t e m za p o m o c ą szkiców, makiet, prototy-
p ó w i t y m p o d o b n y c h , i m o g ą się znacząco zmieniać w konsekwencji testów użyteczności
nawet wtedy, gdy specyfikacja f u n k c j o n a l n a systemu będzie już stabilna. Zbyt wczesne uza-
leżnienie m o d e l u o d szczegółów p o d a t n y c h n a i n t e n s y w n e z m i a n y p o w o d o w a ł o b y w k o n -
sekwencji k o n i e c z n o ś ć j e g o rewizji p o k a ż d e j takiej zmianie, co — m i m o z n a c z n e g o z a a n -
g a ż o w a n i a czasu i wysiłku — nie p r z y n o s i ł o b y p r a k t y c z n i e ż a d n y c h korzyści.

Heurystyki p o m o c n e w identyfikowaniu o b i e k t ó w b r z e g o w y c h

Prawdopodobnymi kandydatami na obiekty brzegowe modelu są między innymi:


• kontrolki interfejsu użytkownika, za pomocą których aktorzy inicjują przypadki użycia
(na przykład ReportEmergencyButton — przycisk, którego kliknięcie powoduje wysłanie raportu
EmergencyReport);
• formularze, za pomocą których użytkownicy wprowadzają dane do systemu (na przykład
EmergencyReportForm — formularz, w którym Fi el dOffi cer umieszcza podstawowe informacje
o zaistniałym wypadku);
• powiadomienia i komunikaty wysyłane przez system do użytkowników (na przykład
Acknowl edgmentNotice — potwierdzenie, jakie Di s p a t c h e r wysyła do Fi el dOffi cera
po przyjęciu raportu o niebezpieczeństwie);
• terminale wykorzystywane przez aktorów uczestniczących w przypadkach użycia do kontaktowania
się z interfejsem systemu (na przykład Di s p a t c h e r S t a t i on — stacja robocza dyspozytora).
Należy unikać wiązania obiektów brzegowych z wizualnymi aspektami interfejsu użytkownika.
W celu opisywania obiektów brzegowych należy bezwzględnie posługiwać się terminologią użytkow-
nika, unikając terminów pochodzących z dziedziny realizacyjnej i dziedziny implementacyjnej.
W tabeli 5.4 przedstawiamy obiekty brzegowe zidentyfikowane na podstawie przypadku
użycia ReportEmergency.

Tabela 5.4. Obiekty brzegowe przypadku użycia ReportEmergency

AcknowledgmentNoti ce Powiadomienie, którego efektem jest wyświetlenie na laptopie


Fi el dOffi cera potwierdzenia wysłanego przez dyspozytora (Di spatcher)
z jego stacji roboczej (Di s p a t c h e r S t a t i on).
Di s p a t c h e r S t a t i o n Komputer (stacja robocza) używana przez dyspozytora (Di spatcher).
ReportEmergencyButton Przycisk, którego kliknięcie przez Fi el dOffi cera inicjuje przypadek
użycia ReportEmergency.
EmergencyReportForm Formularz służący dostarczeniu informacji dla raportu o niebezpieczeństwie
(EmergencyReport). Formularz ten wyświetlony zostaje na laptopie
Fi el dOffi cera, gdy ten aktywuje funkcję „Report Emergency". Formularz
EmergencyReportForm zawiera pola umożliwiające określenie wartości
wszystkich atrybutów raportu EmergencyReport oraz przycisk (lub inną
kontrolkę) przeznaczony do wysłania formularza po jego wypełnieniu.
FieldOfficerStation Laptop używany przez Fi el dOffi cera.
IncidentForm Formularz wykorzystywany do utworzenia nowego obiektu Inci dent
reprezentującego wypadek. Formularz ten wyświetlany jest na stacji
roboczej dyspozytora (Di s p a t c h e r S t a t i o n ) po dotarciu do tej stacji
raportu EmergencyReport. Dyspozytor ( D i s p a t c h e r ) używa tego
formularza także w celu dokonania przydziału zasobów oraz w celu
wysłania potwierdzenia do Fi el dOffi cera.

Zwróćmy uwagę, że obiekt Inci dentForm nie występuje jawnie w przypadku użycia
ReportEmergency. Zidentyfikowaliśmy jego istnienie, uświadamiając sobie, że Dispatcher
potrzebuje odpowiedniego interfejsu do przeglądania raportu przysłanego przez Fi el dOffi cera
i do odesłania mu potwierdzenia. Zauważmy także, iż nazwa tego obiektu odzwierciedla wy-
łącznie jego funkcję, abstrahując od konkretnej realizacji — ta stanowi bowiem część dziedziny
realizacyjnej, o której nie da się po prostu rozmawiać wyłącznie językiem użytkownika.
Identyfikując obiekty encji i obiekty brzegowe, dokonaliśmy pewnego postępu w opisy-
waniu przyszłego systemu — dysponujemy już bowiem zdefiniowanym interfejsem między
aktorami a systemem. Nie dysponujemy jednak jeszcze innymi istotnymi informacjami, ta-
kimi jak na przykład kolejność, w jakiej następują poszczególne interakcje aktorów z systemem.
Zajmijmy się więc trzecią kategorią obiektów odpowiedzialnych za ten i wiele innych aspek-
tów systemu — obiektami sterującymi.

5.4.3. Identyfikacja obiektów sterujących


Obiekty sterujące odpowiedzialne są za koordynowanie obiektów brzegowych z obiektami
encji. Nie posiadają widocznych odpowiedników w świecie rzeczywistym, istnieją raczej w świecie
przypadków użycia. Najczęściej obiekt sterujący tworzony jest podczas inicjowania przypadku
użycia i przestaje istnieć dopiero w momencie, gdy sam przypadek użycia kończy się. Obiekt
sterujący odpowiedzialny jest za kolekcjonowanie informacji pochodzącej od obiektów brze-
gowych i przekazywanie tej informacji odpowiednim obiektom encji.
5.4. Aktywności analizy wymagań: od przypadków użycia do obiektów 223

W naszym przykładowym przypadku użycia Report Emergency utworzymy początkowo


osobne obiekty sterujące dla poszczególnych aktorów, odpowiednio ReportEmergencyControl
dla aktora Fi el dOffi cer i ManageEmergencyControl dla aktora Di spatcher (patrz tabela 5.5).

Tabela 5.5. Obiekty sterujące dla przypadku użycia ReportEmergency

ReportEmergencyControl Zarządza f u n k c j a m i r a p o r t u j ą c y m i na laptopie f u n k c j o n a r i u s z a


(Fi el dOffi c e r S t a t i on). Jest tworzony w momencie, gdy Fi el dOffi cer
kliknie przycisk „Report Emergency". Następnie powoduje utworzenie
obiektu formularza RemergencyReportForin i wyświetlenia go na ekranie
wspomnianego laptopa. Po zatwierdzeniu wspomnianego formularza przez
Fi el dOffi cera niniejszy obiekt kolekcjonuje informację pochodzącą z tego
formularza i przekazuje ją dyspozytorowi (Di spatcher), po czym oczekuje
na otrzymanie potwierdzenia. Gdy potwierdzenie to nadejdzie, niniejszy
obiekt tworzy obiekt A c k n o w l e d g m e n t s i c e i wyświetla go na ekranie
laptopa Fi el dOffi cera.
ManageEmergencyControl Zarządza f u n k c j a m i r a p o r t u j ą c y m i na stacji roboczej dyspozytora
(Di s p a t c h e r S t a t i on). Obiekt ten t w o r z o n y jest w m o m e n c i e , gdy
do stacji roboczej dyspozytora dotrze raport EmergencyReport. Niniejszy
obiekt tworzy następnie obiekt Inci dentForm i wyświetla go na ekranie
stacji dyspozytora. Gdy Di s p a t c h e r utworzy nowy obiekt Inci dent,
przydzieli odpowiednie zasoby ( R e s o u r c e s ) i wyśle potwierdzenie,
niniejszy obiekt przekazuje to potwierdzenie do laptopa funkcjonariusza
(Fi el dOffi c e r S t a t i on).

Decyzja o modelowaniu przepływu sterowania w ramach przypadku użycia Report


^•Emergency za pomocą dwóch obiektów sterujących wynika z faktu, że Fi el dOffi cerStati on
i Di spatcherStati on to dwa podsystemy komunikujące się ze sobą poprzez asynchroniczne
łącze. Tę decyzję można było — co prawda — odłożyć do etapu projektowania systemu, jednak
uwidocznienie już na etapie analizy koncepcji komunikowania się przez asynchroniczne łącze
pozwala uwzględnić w modelu analitycznym istotną sytuację wyjątkową, jaką jest utrata połą-
czenia nawiązanego między obiema stacjami.

Heurystyki pomocne w identyfikowaniu obiektów sterujących


• Zidentyfikuj jeden obiekt sterujący dla każdego przypadku użycia.
• W każdym z przypadków użycia zidentyfikuj po jednym obiekcie sterującym dla każdego
aktora biorącego udział w tym przypadku.
• Czas życia obiektu sterującego powinien rozciągać się na cały przypadek użycia albo na sesję
pojedynczego użytkownika. Jeśli trudno zidentyfikować momenty tworzenia i likwidowania
obiektu sterującego, prawdopodobnie niezbyt precyzyjnie zdefiniowano warunki początkowe
i końcowe dla przypadku użycia.

Tak oto, modelując przypadek użycia ReportEmergency w kategoriach obiektów encji,


brzegowych i sterujących, i spoglądając na ów przypadek z innej perspektywy — strukturali-
zacji, a nie przepływu zdarzeń — zwiększyliśmy poziom jego szczegółowości i ustanowiliśmy
standardową terminologię ułatwiającą odwoływanie się do podstawowych encji dziedziny
aplikacyjnej i systemu. A przecież wciąż modelujemy tę samą funkcjonalność.
224 Rozdział 4. •Zbieraniewymagań

5.4.4. Odwzorowywanie przypadków użycia w obiekty


za pomocą diagramów sekwencji
Istotą diagramu sekwencji jest powiązanie przypadków użycia z obiektami. Diagram taki po-
kazuje, jak zachowanie przypadku użycia (lub scenariusza) rozkłada się na poszczególne obiekty.
Diagramy sekwencji nie są raczej tak dobrym medium komunikacji z użytkownikami jak
przypadki użycia, stanowią bowiem egzemplifikację pewnej formalnej notacji, której zrozu-
mienie wymaga dodatkowej wiedzy; dla użytkowników posiadających taką wiedzę mogą być
jednak bardziej precyzyjnym opisem niż przypadki użycia. Tak czy inaczej, diagramy sekwencji
reprezentują kolejny punkt spojrzenia na funkcjonalność systemu i pomagają programistom
odnajdywać przeoczone dotychczas obiekty lub niejasności w specyfikacji wymagań.
W tej sekcji zbudujemy model ukazujący sekwencje interakcji niezbędnych do realizacji
przypadku użycia. Na rysunkach 5.7, 5.8 i 5.9 widoczne są fragmenty diagramu sekwencji dla
przypadku użycia ReportEmergency. Poszczególne kolumny diagramu sekwencji reprezentują
poszczególne obiekty uczestniczące w przypadku użycia; skrajna lewa kolumna dedykowana
jest aktorowi inicjującemu ów przypadek. Poziome strzałki reprezentują bodźce i komunikaty
przesyłane między obiektami. Kierunek pionowy — z góry na dół — odzwierciedla upływ czasu.
Przykładowo pierwsza strzałka na rysunku 5.7 reprezentuje komunikat press wysłany przez
aktora F i e l d O f f i c e r do obiektu ReportEmergencyButton. Odebranie komunikatu przez
obiekt ReportEmergencyButton powoduje zainicjowanie (aktywowanie) przezeń odpowied-
niej operacji; operacja ta reprezentowana jest przez pionowy prostokąt, z którego wychodzić
mogą kolejne strzałki odpowiadające kolejnym komunikatom. Wysokość prostokąta odpowiada
czasowi trwania rzeczonej operacji. Na rysunku 5.7 operacja zainicjowana przez komunikat
press powoduje wysłanie komunikatu create do klasy ReportEmergencyControl. Operację
tę można uważać za rodzaj usługi, jaką obiekt świadczyć może na rzecz innych obiektów. Na
diagramach sekwencji uwidoczniony jest także czas życia obiektów. Obiekty, które istnieją już
przed zainicjowaniem przypadku użycia przez aktora, rysowane są u samej góry diagramu.
Obiekty tworzone w wyniku interakcji wskazywane są przez strzałki odnośnych komunikatów,
opatrzone etykietą «creates. Unicestwienie instancji obiektu symbolizowane jest przez znak
przekreślenia, umieszczony na odpowiedniej wysokości, odpowiadającej momentowi, w którym
unicestwienie to następuje. Między prostokątem reprezentującym obiekt a znakiem przekre-
ślenia oznaczającym jego unicestwienie (lub dolną linią przypadku użycia, gdy obiekt nie jest
niszczony w trakcie jego trwania) rozciąga się pionowa linia przerywana, reprezentująca prze-
dział czasu, w którym dany obiekt może przyjmować komunikaty; oczywiście, linia taka nie
może wystąpić poniżej znaku przekreślenia. Na rysunku 5.7 obiekt klasy ReportEmergency
"-^Form tworzony jest w wyniku odebrania przez tę klasę komunikatu «create» wysłanego
przez obiekt ReportEmergencyControl. Obiekt ReportEmergencyForm przestaje istnieć zaraz
po tym, jak formularz ReportEmergencyForm zostaje wysłany (niszczy go ten sam obiekt, który
go utworzył).
Generalnie druga kolumna diagramu sekwencji reprezentuje obiekt brzegowy, z którym
współdziała aktor inicjujący przypadek użycia; na rysunku 5.7 jest to obiekt ReportEmergency
^•Button. Trzecia kolumna przeznaczona jest na obiekt sterujący, zarządzający całą resztą
przypadku (ReportEmergencyControl na rysunku 5.7). Ów obiekt sterujący tworzy inne obiekty
brzegowe i może współdziałać z innymi obiektami sterującymi (na rysunku 5.7 z obiektem
ManageEmergencyControl).
5.4. Aktywności analizy wymagań: od przypadków użycia do obiektów 225

Rysunek 5.7. Diagram sekwencji dla przypadku użycia ReportEmergency

Rysunek 5.8. Diagram sekwencji dla przypadku użycia ReportEmergency (kontynuacja rysunku 5.7)

W diagramie na rysunku 5.8widoczny jest obiekt encji Acknowl edgment, o którym


pierwotnie zapomnieliśmy (patrz tabela 5.3). Obiekt Acknowledgment różni się od obiektu
AcknowledgmentNotice: ten pierwszy reprezentuje informację związaną z potwierdzeniem
raportu i tworzony jest wcześniej od tego drugiego. Przy okazji obiektu Acknowledgment
uświadamiamy sobie ponadto, że opis przypadku użycia ReportEmergency w tabeli 5.2,
przedstawiony na rysunku 5.6, jest niekompletny: wspomina się w nim jedynie o istnieniu
potwierdzenia raportu, nie precyzując, jaka informacja ma być w nim zawarta. Po konsul-
tacjach z klientem w tej kwestii dodajemy obiekt Acknowledgment do modelu analityczne-
go (patrz tabela 5.6), a przypadek użycia ReportEmergency rozszerzamy o kolejny szczegół
(patrz tabela 5.7)
226 Rozdział 4. •Zbieraniewymagań

Rysunek 5.9. Diagram sekwencji dla przypadku użycia ReportEmergency (kontynuacja rysunku 5.8)

Tabela 5.6. Obiekt Acknowl edgment przypadku użycia ReportEmergency

Acknowl edgment Potwierdzenie, odpowiedź dyspozytora (Di spatcher) na raport EmergencyReport


wysłany przez Fi el dOffi cera. Za pomocą tego potwierdzenia Di spatcher oznajmia
Fi el dOffi cerowi, że otrzymał raport, utworzył w bazie reprezentujący go obiekt
i przydzielił zasoby do jego obsługi. Potwierdzenie zawiera też wyszczególnienie
przydzielonych zasobów oraz oszacowanie czasu oczekiwania na ich przybycie.

Konstruując diagramy sekwencji, modelujemy nie tylko kolejność interakcji między


obiektami, lecz także podział zachowania systemu między przypadki użycia. W ten sposób
przypisujemy każdemu obiektowi swoisty zakres odpowiedzialności, w postaci określonego
zbioru operacji. Operacje te mogą być współdzielone między wszystkie przypadki użycia,
w których dany obiekt uczestniczy; stąd bardzo ważne zastrzeżenie: definicja obiektu uczest-
niczącego w kilku przypadkach użycia powinna być identyczna we wszystkich, innymi słowy,
jeśli dana operacja danego obiektu pojawia się w kilku przypadkach użycia, jej zachowanie
powinno być w każdym z nich takie samo.
Współdzielenie operacji między kilka przypadków użycia umożliwia programistom re-
dukowanie redundancji w specyfikacji wymagań, przyczynia się więc do zachowania jej spój-
ności. Należy jednak pamiętać, że klarowność i czytelność jest ważniejsza od eliminowania
redundancji: zbyt duża fragmentacja zachowania na wiele operacji jedynie komplikuje specy-
fikację i sprawia, że staje się mniej zrozumiała.
Na etapie analizy diagramy sekwencji pomagają identyfikować nowe obiekty uczestni-
czące oraz przeoczone aspekty zachowań. Jako że diagramy sekwencji opisują zachowanie
systemu w sposób wysokopoziomowy, wszelkie kwestie implementacyjne, z wydajnością sys-
temu włącznie, nie powinny być w ogóle rozważane na tym etapie. Ponieważ konstruowanie
diagramów interakcji jest czasochłonne samo z siebie, programiści powinni w pierwszym
rzędzie koncentrować się na problematycznych i nieprecyzyjnie sformułowanych elementach
funkcjonalności systemu. Rysowanie diagramów interakcji dla tych części systemu, które są
bardzo proste lub dostatecznie jasno zdefiniowane, chociaż nie wydaje się dobrą inwestycją
czasu i wysiłku, także powinno być wykonywane, może bowiem uwidocznić konieczność pod-
jęcia pewnych kluczowych decyzji, których dotąd nie dostrzegano.
5.4. Aktywności analizy wymagań: od przypadków użycia do obiektów 227

Tabela 5.7. Dalsze uszczegółowienie przypadku użycia ReportEmergency. Zidentyfikowanie istnienia


obiektu Acknowledgment umożliwiło wykrycie luki w opisie — brak szczegółów na temat informacji
zawartej w potwierdzeniu reprezentowanym przez ten obiekt. Uzupełnienie wyróżniono kursywę

Nazwa przypadku użycia ReportEmergency


Warunek wstępny 1. Fi el dOf f i cer aktywuje funkcję „Report Emergency" na swym terminalu.

Przepływ zdarzeń 2. System FRI END wyświetla w odpowiedzi stosowny formularz. Formularz
ten zawiera pola określające między innymi rodzaj niebezpieczeństwa
(ogólne, pożar, komunikacyjne), lokalizację zdarzenia, jego opis, żądanie
zasobów i udział materiałów niebezpiecznych.

3. Fi el dOf f i c e r wypełnia f o r m u l a r z z obowiązkowymi p o l a m i


reprezentującymi typ zdarzenia i jego krótki opis. Fi el dOf f i cer określa
także możliwe sposoby pierwszej reakcji na zagrożenie i wymagane
do tej reakcji zasoby. Po wypełnieniu formularza Fi el dOf f i cer wysyła
go do systemu, klikając przycisk Send report. W rezultacie system
FRIEND powiadamia dyspozytora (Dispatcher) o zdarzeniu.

4. Di s p a t c h e r analizuje otrzymane od Fi el dOf f i cera informacje zawarte


w formularzu, po czym tworzy nowy obiekt I nci dent w bazie, realizując
przypadek użycia Openlncident. Di s p a t c h e r określa sposób reakcji,
przyporządkowuje niezbędne zasoby (zgodnie z przypadkiem użycia
ATlocateResources) i zatwierdza raport, wskutek czego system FRIEND
informuje aktora Fi el dOf f i c e r o zatwierdzeniu raportu i przekazuje
m u i n f o r m a c j ę od dyspozytora. Potwierdzenie (Acknowl edgment,)
otrzymane od dyspozytora informuje Fi el dOf f i cera, że dyspozytor
otrzymał raport EmergencyReport, utworzył w bazie obiekt reprezentujący
wypadek (Inci d e n t ) i przydzielił zasoby (na przykład wóz strażacki)
niezbędne do jego obsługi; w potwierdzeniu zawarta jest też informacja
0 szacunkowym czasie oczekiwania na przybycie grupy interwencyjnej
1 wyszczególnienie przydzielonych zasobów.

Warunki koricowe 5. Fi el dOff i c e r otrzymał potwierdzenie od dyspozytora.

Heurystyki wspomagające rysowanie diagramów sekwencji


® Pierwsza kolumna diagramu powinna reprezentować aktora inicjującego odnośny
przypadek użycia.

• Drugą kolumnę należy poświęcić obiektowi brzegowemu, za pośrednictwem którego wspomniany


aktor inicjuje przypadek użycia.
• Trzecia kolumna powinna być przeznaczona dla obiektu sterującego, zarządzającego
pozostałą częścią przypadku użycia.
® Obiekty sterujące tworzone są przez obiekty brzegowe, za pośrednictwem których aktorzy
inicjują przypadki użycia.

• Wewnątrz przypadku użycia obiekty brzegowe tworzone są przez obiekty sterujące.


• Obiekty encji wykorzystywane są przez obiekty brzegowe i sterujące.

• Obiekty encji nigdy nie korzystają z obiektów brzegowych i sterujących; ułatwia to


współdzielenie obiektów encji przez przypadki użycia.
228 Rozdział 4. •Zbieraniewymagań

5.4.5. Modelowanie interakcji między obiektami za pomocą kart CRC


Alternatywnym narzędziem identyfikowania interakcji między obiektami są karty CRC,
opisane przez K. Becka i W. Cunninghama [Beck i Cunningham, 1989]; akronim CRC pochodzi
od słów Class, Responsibilities i Collaborators („klasa, odpowiedzialność i [klasy] współdziała-
jące"). Zaprojektowane zostały jako narzędzie ułatwiające studiowanie technik obiektowych
zarówno nowicjuszom, jak i projektantom doświadczonym, lecz nieobeznanym z techni-
kami obiektowymi. Każda klasa modelu reprezentowana jest przez kartę indeksową (zwaną
kartą CRC). U góry karty widnieje nazwa klasy, w lewej kolumnie zakres odpowiedzialności
klasy, w prawej zaś znajduje się lista klas, za pomocą których dana klasa spełnia swe funkcje
wymienione w lewej. Na rysunku 5.10 widoczne są dwie karty indeksowe reprezentujące klasy
ReportEmergencyControl i Incident.

Rysunek 5.10. Przykładowe karty CRC

Karty indeksowe przydają się w zespołowych sesjach modelowania. W trakcie takiej sesji,
w której uczestniczą zwykle programiści i eksperci z dziedziny aplikacyjnej, analizowany jest
scenariusz i identyfikowane są kolejne klasy biorące w nim udział; dla każdej nowo ziden-
tyfikowanej klasy na tablicy pojawia się nowa karta indeksowa. Dla każdej klasy negocjo-
wany jest zakres odpowiedzialności, identyfikowane są też zależności od pozostałych klas.
Karty mogą być modyfikowane lub nawet usuwane na bok tablicy jako nieaktualne; nie są
jednak z tablicy usuwane, mogą bowiem za chwilę znowu okazać się przydatne, gdy pojawią
się nowe pomysły.
Karty CRC i diagramy sekwencji to dwie różne metody opisywania tych samych aktyw-
ności. Diagramy bardziej nadają się do modelowania w pojedynkę oraz dla celów dokumenta-
cyjnych, gdyż są bardziej precyzyjne i bardziej zwarte; karty CRC sprawdzają się natomiast
lepiej w grupowym („burza mózgów") budowaniu i ulepszaniu struktury obiektowej przypad-
ków użycia, ponieważ łatwiej je tworzyć i modyfikować.

5.4.6. Identyfikacja skojarzeń


Podczas gdy diagramy sekwencji umożliwiają programistom reprezentowanie interakcji mię-
dzy obiektami w układzie chronologicznym, diagramy klas ukazują zależności między obiek-
tami. W rozdziale 2. „Modelowanie w języku UML" zdefiniowaliśmy notację UML używaną
w tej książce na oznaczenie różnych artefaktów produktu (aktywności, produktów docelowych
5.4. Aktywności analizy wymagań: od przypadków użycia do obiektów 229

i tym podobnych), teraz omówimy zastosowanie diagramów klas do reprezentowania sko-


jarzeń między obiektami. W sekcji 5.4.8 zajmiemy się zastosowaniem diagramów klas do re-
prezentowania atrybutów obiektów.
Skojarzenie przedstawia relację między dwiema (lub kilkoma) klasami, przykładowo
klasa FieldOfficer skojarzona jest z klasą EmergencyReport (patrz rysunek 5.11), ponieważ
funkcjonariusz (FieldOfficer) sporządza raporty o niebezpieczeństwie (EmergencyReport).
Identyfikowanie skojarzeń między klasami służy dwóm celom. Po pierwsze, czyni model
bardziej czytelnym, poprzez uwidocznienie istniejących między klasami zależności (na przy-
kład raporty EmergencyReport mogą być sporządzane przez funkcjonariuszy [Fi el dOffi cer]
lub dyspozytorów [Dispatcher]). Po drugie, umożliwia programistom wykrywanie przypad-
ków granicznych związanych z tymi zależnościami. Przypadki graniczne wiążą się najczęściej
z wyjątkami, które muszą być jednoznacznie opisane w modelu; jest na przykład intuicyjnie
jasne, że dany funkcjonariusz (FieldOfficer) może sporządzać wiele raportów (Emergency
^Report), mniej oczywisty jest natomiast charakter zależności odwrotnych: czy dany raport
może mieć kilku autorów, czy też musi być sporządzony przez dokładnie jednego funkcjona-
riusza. Czy dopuszczalne są raporty anonimowe? Tego rodzaju kwestie powinny być na etapie
analizy szczegółowo przedyskutowane z klientem i użytkownikami.

Rysunek 5.11. Przykład skojarzeń między klasami

Każde skojarzenie posiada następujące atrybuty:

• nazwę związaną z jego charakterem (sporządza na rysunku 5.11); jest ona opcjo-
nalna i nie musi być globalnie unikalna;
• role identyfikujące funkcje każdego z uczestników skojarzenia (autor i dokument na
rysunku 5.11);
• krotność precyzującą dopuszczalną liczbę instancji klas po każdej ze stron skojarze-
nia (gwiazdka na rysunku 5.11 oznacza, że funkcjonariusz ( F i e l d O f f i c e r ) może
sporządzić dowolną liczbę raportów (EmergencyReport) lub nie sporządzić żadnego;
liczba 1 oznacza natomiast, że każdy raport ma dokładnie jednego autora).

W początkowej fazie analizy skojarzenia między klasami są niezwykle ważne, odkrywają


bowiem nowe fakty z zakresu dziedziny aplikacyjnej. Zgodnie z tabelą 5.1, skojarzeń między
obiektami należy poszukiwać w opisie słownym wśród czasowników i fraz czasownikowych
określających stan rzeczy („posiada", „jest częścią...", „zarządza", „raportuje do...", „jest wy-
zwalany przez...", „komunikuje się z . . „ z a w i e r a " ) . Konkretny czasownik determinuje zwykle
nazwę skojarzenia i role po obu jego stronach.
230 Rozdział 4. •Zbieraniewymagań

Heurystyki pomocne przy identyfikowaniu skojarzeń


• Analizuj frazy czasownikowe.
• Używaj precyzyjnych nazw i precyzyjnie określaj role skojarzeń.
• Wykorzystuj kwalifikowanie 2 skojarzeń, wszędzie gdzie to tylko możliwe, do identyfikowania
przestrzeni nazw i atrybutów kluczowych.
• Nie uwzględniaj skojarzeń, które mogą być wyprowadzone na podstawie innych skojarzeń.
• Do momentu ustabilizowania zbioru skojarzeń nie przywiązuj zbyt dużej wagi do ich krotności.
• Zbyt wielka liczba skojarzeń sprawia, że model staje się nieczytelny.

Początkowo model może zawierać nadmiar skojarzeń, jeśli programiści zastosują się do
heurystyk opisywanych w tabeli 5.1; przykładowo w diagramie na rysunku 5.12 klasa repre-
zentująca wypadek (Incident) uwikłana jest w dwa skojarzenia: z raportem (EmergrncyReport)
— ponieważ zaistnienie wypadku skutkuje utworzeniem raportu — i z funkcjonariuszem
(Fi el dOffi cer), który wypadek raportuje. Drugie z tych skojarzeń jest jednak redundantne,
wynika bowiem z dwóch pozostałych — nie ma potrzeby jego reprezentowania explicite.
Utrzymywanie zbędnych skojarzeń ma niekorzystny wpływa na model, bo wprowadza do
niego redundancję i utrudnia jego zrozumienie.

Rysunek 5.12. Przykład redundantnych skojarzeń: otrzymanie raportu o wypadku (EmergencyReport)


powoduje utworzenie przez dyspozytora obiektu reprezentującego ten wypadek ( I n c i d e n t ) . Ponie-
waż wspomniany raport skojarzony jest z funkcjonariuszem (Fi el dOffi c e r ) będącym jego autorem,
skojarzenie między funkcjonariuszem ( F i e l d O f f i c e r ) a wypadkiem ( I n c i d e n t ) jest samoistnym
efektem wtórnym obu wymienionych skojarzeń — jest więc od nich zależne i jego umieszczenie na
diagramie wprowadza do modelu redundancję

Większość obiektów encji posiada unikalną charakterystykę, wykorzystywaną do ich


identyfikowania przez aktorów. Funkcjonariusz (FieldOfficer) i dyspozytorzy (Dispatcher)
identyfikowani są na podstawie n u m e r u odznaki, wypadki (Incident) i raporty (Report)
opatrywane są unikalnymi numerami, a przy archiwizowaniu dodatkowo datą. Gdy model
analityczny obrośnie już w sporą liczbę klas, programiści powinni zastanowić się nad tym,
jak i w jakim kontekście instancje każdej z tych klas identyfikowane są przez aktorów. Czy
na przykład numer odznaki policjanta jest niepowtarzalny na całym świecie? A może tylko
w obrębie jednego miasta? Czy tylko jednego komisariatu? Tego typu wątpliwości dla da-
nej klasy łatwiej się rozstrzyga, przechodząc ciąg skojarzeń konieczny do uzyskania dostę-
pu do konkretnej instancji tej klasy.

2
Kwalifikowanie skojarzeń autorzy opisują w sekcji 2.4.2 — pTzyp. tłum.
5.4. Aktywności analizy wymagań: od przypadków użycia do obiektów 231

5.4.7. Identyfikacja agregacji


Agregacja jest specjalnym rodzajem skojarzenia, reprezentującym relację typu „całość-część".
Przykładowo oddział straży pożarnej (FireStation) tworzy pewna liczba strażaków (Fire
^•Fighter), oddział ten dysponuje pewną liczbą wozów bojowych (Ambulance), urządzeń ga-
śniczych (Fi reEngi ne) i samochodem komendanta (LeadCar). Z kolei województwo składa się
z pewnej liczby powiatów, te zaś z pewnej liczby gmin (patrz rysunek 5.13). Na diagramie klas
agregacja wyróżniana jest za pomocą rombu po stronie „całość" skojarzenia.

Rysunek 5.13. Przykłady agregacji

Istnieją dwa typy agregacji — kompozycyjna i współdzielona. W agregacji kompo-


zycyjnej istnienie części uwarunkowane jest istnieniem całości — powiat jest zawsze częścią
województwa, gmina jest częścią powiatu; nie jest możliwe współdzielenie gminy między dwa
powiaty (przynajmniej w obecnym podziale administracyjnym). Agregacja kompozycyjna
oznaczana jest wypełnionym rombem. Romb pusty oznacza agregację współdzieloną, zgodnie
z którą część, choć powiązana z całością, może istnieć niezależnie od niej: choć wóz strażacki
jest w danej chwili na wyposażeniu konkretnego oddziału straży, może być w razie potrzeby
przekazywany innym oddziałom i generalnie może też istnieć niezależnie, bez związku z kon-
kretnym oddziałem.
Agregacje wprowadzają do modelu informację na temat koncepcji „część-całość", które
dzięki temu mogą być modelowane w postaci struktur drzewiastych lub innych paradygmatów
hierarchicznych. Agregacje stosowane są często w modelowaniu interfejsu użytkownika, by
łatwiej było nawigować wśród złożonej struktury obiektów: przykładowo dialog wyszukiwania
miejscowości na mapie może odbywać się w trzech etapach, obejmujących określenie (kolejno)
województwa, powiatu i gminy.
Tak jak w przypadku wielu innych koncepcji modelowania, również i w przypadku iden-
tyfikowania agregacji należy zachować umiar: jeżeli nie jesteśmy pewni, czy dana relacja
między klasami jest na pewno relacją „część-całość", bezpieczniej modelować tę relację w po-
staci skojarzenia „jeden na wiele", które w przyszłości, po lepszym zrozumieniu dziedziny
aplikacyjnej, być może zamienione zostanie na agregację3.

3
Różnicę między agregacją a skojarzeniem „jeden na wiele" w ogólności autorzy wyjaśniają w sekcji
2.4.2 — przyp. tłum.
232 Rozdział 4. •Zbieraniewymagań

5.4.8. Identyfikacja atrybutów


Atrybuty to właściwości poszczególnych obiektów danej klasy. Przykładowo każdy raport
EmergencyReport, zgodnie z opisem w tabeli 5.3, określa typ wypadku, jego lokalizację i krótki
opis — i takie też są atrybuty obiektów klasy EmergencyReport (patrz rysunek 5.14). Atrybu-
tom tym nadawane są wartości w momencie, gdy Fi el dOf f i cer tworzy formularz raportu;
wartości te są następnie przetwarzane przez system. Spośród zidentyfikowanych właściwości
obiektów danej klasy na atrybuty kwalifikują się tylko te, które mają znaczenie dla systemu:
przykładowo funkcjonariusz ( F i e l d O f f i c e r ) posiada numer ubezpieczenia, który jednak
pozostaje bez związku z systemem FRIEND i jako taki nie znajduje się na liście atrybutów obiektu
Fi el dOf f i cer. Istotny jest za to numer odznaki funkcjonariusza, który reprezentowany jest
w postaci atrybutu badgeNumber klasy Fi el dOf f i cer.

Rysunek 5.14. Atrybuty klasy EmergencyReport

Nie są atrybutami właściwości reprezentowane przez obiekty — nie ma więc wśród


atrybutów klasy EmergencyReport atrybutu określającego autora raportu, ten bowiem repre-
zentowany jest w postaci skojarzenia z klasą Fi el dOff i cer. Programiści powinni więc ziden-
tyfikować jak najwięcej skojarzeń między klasami, zanim przystąpią do identyfikowania atry-
butów poszczególnych klas. Każdy atrybut posiada trzy następujące cechy.

• Nazwę identyfikującą go w obrębie klasy; przykładowo typ raportu reprezentowany


jest w klasie EmergencyReport przez atrybut o nazwie reportType, zaś atrybut o na-
zwie emergency Type reprezentuje typ wypadku (pożar, wypadek drogowy, inny wy-
padek). By uniknąć wieloznaczności, żadnemu z tych atrybutów nie należy nadawać
ogólnej nazwy type.
• Krótki opis.
• Typ oznaczający zbiór wartości, jakie może przyjmować dany atrybut; przykładowy
atrybut description klasy EmergencyReport jest łańcuchem znaków (String), zaś
atrybut emergency Type jest atrybutem typu wyliczeniowego, czyli może przyjmować
wartości z predefiniowanego zbioru f i re (pożar), t r a f f i c (wypadek drogowy) i other
(inny wypadek). W języku UML typy atrybutów bazują na predefiniowanych typach
podstawowych tego języka.

Atrybuty identyfikować można w nieformalnym opisie za pomocą cytowanych już


heurystyk Abbotta z tabeli 5.1. W szczególności rzeczownik w dopełniaczu („opis wypadku")
wskazywać może prawdopodobnego kandydata na atrybut („opis"). W przypadku obiektu encji
każda jego właściwość, która zapamiętywana jest w systemie w sposób trwały, jest prawdopo-
dobnym atrybutem.
5.4. Aktywności analizy wymagań: od przypadków użycia do obiektów 233

Atrybuty należą do najmniej stabilnych części modelu. Bywa, że są identyfikowane i do-


dawane do modelu nawet w fazie implementacji, gdy użytkownicy wyciągną wnioski z przed-
stawionej wersji testowej. Jeżeli atrybuty te są związane z dodatkową funkcjonalnością systemu,
konieczna jest rewizja znacznej ilości wykonanej już pracy. Z tego względu nie ma sensu trwo-
nić czasu i innych zasobów na wyczerpujące identyfikowanie atrybutów dotyczących mniej
istotnych aspektów systemu — mogą one zostać dodane później, po ocenie przez użytkowni-
ków modelu analitycznego lub szkiców interfejsu.

Heurystyki pomocne w identyfikowaniu atrybutów4


• Szukaj rzeczowników w dopełniaczu.
• Dla obiektów encji każda elementarna informacja o stanie jest prawdopodobnym atrybutem.
• Opatruj każdy atrybut treściwym opisem.
• Właściwości będące obiektami nie nadają się na atrybuty — są reprezentowane przez
skojarzenia (patrz sekcja 5.4.6).
• Nie zagłębiaj się zbytnio w subtelności obiektu, jeśli jego struktura nie jest jeszcze
ustabilizowana.

5.4.9. Modelowanie zachowania poszczególnych obiektów


uzależnionego od ich stanu
Diagramy sekwencji uwidaczniają podział funkcjonalności między obiekty i operacje reali-
zujące tę funkcjonalność; reprezentują one zachowanie systemu z perspektywy konkretnego
przypadku użycia. Diagramy stanów reprezentują natomiast zachowanie systemu z perspek-
tywy konkretnego obiektu; ten punkt widzenia pozwala programistom na tworzenie bardziej
sformalizowanych opisów obiektów i w konsekwencji identyfikowanie przeoczonych przy-
padków użycia. Skupiając się na poszczególnych stanach obiektu, programiści mogą odkrywać
nowe elementy jego zachowania. Przykładowo, analizując każde przejście między stanami,
identyfikują akcję aktora wyzwalającą to przejście, a następnie odnajdują w przypadku użycia
krok opisujący tę akcję.
Nie jest konieczne sporządzanie diagramu stanów dla każdej klasy; warte takiej analizy
są jedynie obiekty o długim czasie życia oraz te o zachowaniu silnie determinowanym przez
stan wewnętrzny. Dotyczy to najczęściej obiektów sterujących, rzadziej obiektów encji i nigdy
obiektów brzegowych.
Na rysunku 5.15 widnieje diagram stanów dla klasy Incident. Analiza tego diagramu
nakazuje programistom sprawdzenie, czy istnieją przypadki użycia odzwierciedlające doku-
mentowanie, zamykanie i archiwizowanie obsługi wypadków. Ulepszając definicję każdego
stanu, programiści mogą dodawać szczegóły do różnych akcji użytkowników wpływających
na ten stan; przykładowo, gdy obsługa wypadku (Inci dent) znajduje się w stanie Acti ve, funk-
cjonariusz (FieldOfficer) powinien mieć możliwość żądania dodatkowych zasobów, a dys-
pozytor (Di spatcher) musi mieć możliwość ich przydzielania do „aktywnych" wypadków.

4
Na podstawie pracy ]. Rumbaugha, M. Błahy, W. Premerlaniego, F. Eddyego, W. Lorensena [Rumbaugh
i in., 1991].
234 Rozdział 4. •Zbieraniewymagań

Rysunek5.15. Diagram stanów dla klasy I n c i d e n t

5.4.10. Modelowanie relacji dziedziczenia między obiektami


Generalizacja wykorzystywana jest do eliminowania redundancji z modelu analitycznego. Jeśli
dwie klasy (lub więcej) współdzielą te same atrybuty lub to samo zachowanie, podobieństwo
to kwalifikuje się do konsolidacji w ramach superklasy. Przykładowo zarówno funkcjonariusz
(FieldOfficer), jak i dyspozytor (Dispatcher) identyfikowani są na podstawie numeru od-
znaki (unikalnego w całym mieście). Identyfikacja na podstawie odznaki jest cechą charakte-
rystyczną dla oficera policji, niezależnie od tego, do jakich funkcji ów oficer jest przydzielony:
by uwidocznić ten fakt w modelu, definiujemy abstrakcyjną superldasę PoliceOfficer, dla
której klasy F i e l d O f f i c e r i Dispatcher są subklasami (patrz rysunek 5.16).

Rysunek 5.16. Przykład relacji dziedziczenia


5.4. Aktywności analizy wymagań: od przypadków użycia do obiektów 235

5.4.11. Przeglądy modelu analitycznego


Model analityczny budowany jest przyrostowo i iteracyjnie, rzadko więc jest poprawny czy na-
wet kompletny za pierwszym razem. Zwykle potrzeba wielu iteracji zanim model ten stanie się
bliski poprawnej specyfikacji, użytecznej dla programistów projektujących i implementujących
system. I tak na przykład przeoczenia wykryte w trakcie analizy prowadzą do rozszerzania ist-
niejących przypadków użycia i definiowania nowych, co wiąże się z pozyskiwaniem dodat-
kowej wiedzy i zbieraniem dodatkowych wymagań od klienta i użytkowników.
Gdy intensywność zmian w modelu stanie się minimalna, a zakres tych zmian zloka-
lizowany, można uznać, że model analityczny jest stabilny. Model ten jest wówczas przeglą-
dany, najpierw przez programistów (w ramach przeglądu wewnętrznego), następnie wspólnie
przez programistów i klienta. Celem tego przeglądu jest upewnienie się, że specyfikacja wy-
magań jest poprawna, kompletna, spójna i jednoznaczna. Programiści i klient oceniają także
poszczególne wymagania pod kątem ich realności i weryfikowalności. Oczywiście, programiści
powinni być przygotowani na konieczność zmian w specyfikacji wskutek wykrytych błędów
i zmotywowani do wykrywania tych błędów jak najwcześniej. Wspomniany przegląd powi-
nien być wspomagany kontrolną listą pytań — oto przykład takiej listy, zaczerpnięty z prac
zbiorowych [Jacobson i in., 1999] i [Rumbaugh i in., 1991].

1. Pytania dotyczące spójności modelu:


• Czy słownik obiektów encji jest zrozumiały dla użytkownika?
• Czy zdefiniowane klasy abstrakcyjne odpowiadają koncepcjom użytkowników?
• Czy wszystkie opisy sporządzone są w zgodzie z definicjami użytkownika?
• Czy wszystkie obiekty encji i obiekty brzegowe posiadają znaczące nazwy w formie
rzeczowników?
® Czy wszystkie obiekty sterujące posiadają znaczące nazwy w formie czasowników?
® Czy przewidziano obsługę dla wszystkich opisanych błędów i sytuacji wyjątkowych?
2. Pytania dotyczące kompletności modelu:
® Czy każdy z obiektów wykorzystywany jest przez chociaż jeden przypadek użycia?
W którym przypadku użycia dany obiekt jest tworzony? Modyfikowany? Nisz-
czony? Czy jest dostępny z poziomu któregoś z obiektów brzegowych?
® Czy każdemu z atrybutów jest nadawana wartość? Jakie są typy poszczególnych
atrybutów? Które z atrybutów powinny być kwalifikatorami skojarzeń?
® Czy każde ze skojarzeń jest potrzebne? Kiedy jest wykorzystywane dane skojarzenie?
Dlaczego wybrano dla niego takie, a nie inne krotności? Które ze skojarzeń
„jeden na wiele" i „wiele na wiele" powinny być kwalifikowane?
• Czy każdy obiekt sterujący opatrzony jest wystarczającym zestawem skojarzeń do
osiągnięcia wszystkich obiektów uczestniczących w danym przypadku użycia?
3. Pytania dotyczące spójności modelu:
® Czy każda klasa i każdy przypadek użycia posiadają unikalną nazwę?
® Czy encje (przypadki użycia, klasy, atrybuty) o podobnych nazwach reprezentują
podobne koncepcje?
236 Rozdział 4. •Zbieraniewymagań

® Czy istnieją dwa obiekty o podobnych atrybutach i podobnych skojarzeniach,


należące do różnych hierarchii generalizacji?
4. Pytania dotyczące realistyczności modelu:
• Czy w systemie przewidziana jest jakaś niezwykła cecha lub funkcjonalność?
Jeśli tak, to czy weryfikowano rację jej bytu za pomocą prototypów lub analizy
opłacalności?
• Czy możliwe jest zrealizowanie wymagań dotyczących wydajności i niezawod-
ności? Czy realność tych wymagań została potwierdzona testami prototypów na
docelowej platformie sprzętowej?

5.4.12. Podsumowanie analizy


Zbieranie wymagań jest procesem wybitnie iteracyjnym i przyrostowym. Elementy funkcjonal-
ności uzgadniane są (w postaci szkiców) między programistami a klientem. Klient formułuje
nowe wymagania, poddaje krytycznej ocenie obecny kształt opisu funkcjonalności i modyfi-
kuje istniejące wymagania. Programiści weryfikują swe pojęcie o wymaganiach pozafunkcyj-
nych za pomocą prototypów i analizy technologicznej. Początkowo zbieranie wymagań przy-
pomina burze mózgów: w miarę jak opis systemu rośnie objętościowo, a wymagania stają się
coraz bardziej konkretne, programiści zmuszeni są do rozszerzania i modyfikowania modelu
analitycznego w kierunku jego lepszej organizacji, ułatwiającej zapanowanie nad złożonością
informacji.
Na rysunku 5.17 przedstawiono typową sekwencję aktywności etapu analizy. Użytkownicy,
klient i programiści opracowują wspólnie początkowy model przypadków użycia. Identy-
fikują rozmaite koncepcje i tworzą słownik związanych z tymi koncepcjami klas i obiektów.
Obie te aktywności omawialiśmy w poprzednim rozdziale, ten rozdział poświęcony jest pozo-
stałym. Programiści klasyfikują obiekty uczestniczące w przypadkach użycia w trzech ka-
tegoriach: obiektów encji, brzegowych i sterujących (o czym pisaliśmy kolejno w sekcjach 5.4.1,
5.4.2 i 5.4.3). Tworzone dalej diagramy sekwencji pomagają zauważyć przypadki ewentualnego
przeoczenia klas czy obiektów (diagramom sekwencji poświęciliśmy sekcję 5.4.4). Gdy wszystkie
obiekty encji zostaną opatrzone znaczącymi nazwami i treściwie opisane, model analityczny
powinien pozostać stosunkowo stabilny podczas kolejnych kroków jego ulepszania.
Definiowanie skojarzeń (patrz sekcja 5.4.6), definiowanie atrybutów (patrz sekcja 5.4.8)
i definiowanie zachowania zależnego od stanu (patrz sekcja 5.4.9) to aktywności składające się
na ulepszanie modelu. Wszystkie one wykonywane są w ścisłym związku: stany i skojarzenia
poszczególnych obiektów identyfikowane są na podstawie diagramów sekwencji, po czym
ewentualne zmiany funkcjonalne odzwierciedlane są w przypadkach użycia (poprzez modyfi-
kowanie istniejących lub tworzenie nowych), dzięki czemu obraz funkcjonalny systemu staje
się coraz bardziej kompletny.
Konsolidacja modelu (patrz sekcja 5.4.10) to wprowadzanie doń kwalifikacji skojarzeń,
generalizowanie koncepcji (i definiowanie abstrakcyjnych superklas) oraz eliminowanie
redundancji. Wreszcie, podczas przeglądu modelu (patrz sekcja 5.4.11) klient, użytkownicy
i programiści weryfikują ów model pod kątem poprawności, spójności, kompletności i reali-
styczności. W harmonogramie projektu należy zaplanować wiele takich przeglądów, z inten-
cją należytego sprecyzowania zebranych wymagań. Kiedy jednak model osiągnie stadium,
4.5. Zarządzaniez b i e r a n i e mwymagań 237

Rysunek 5.17. Aktywności etapu analizy

w którym większość zmian będzie mieć charakter kosmetyczny, należy przystąpić do następ-
nego etapu — projektowania systemu. W procesie zbierania wymagań przychodzi taki mo-
ment, że nie sposób już przewidzieć kolejnych problemów bez prototypowania, analizy uży-
teczności, przeglądu dostępnych technologii czy (właśnie) projektowania systemu. Dążenie do
nadmiernej szczegółowości może stać się syzyfową pracą, bowiem niektóre ze szczegółów przy
następnej zmianie mogą okazać się nieaktualne. Menedżer projektu powinien rozpoznać ten
moment i zarządzić przejście do następnej fazy realizacji.

5.5. Zarządzanie analizą wymagań


W tej sekcji przedyskutujemy problematykę zarządzania analizą wymagań w projekcie re-
alizowanym przez wiele zespołów. W tej sytuacji podstawowym wyzwaniem jest utrzymanie
spójności między dużą liczbą wykorzystywanych zasobów — po zakończeniu analizy wień-
czący ją dokument powinien opisywać jeden spójny system, w sposób zrozumiały dla poje-
dynczego człowieka.
238 Rozdział 4. •Zbieraniewymagań

Rozpoczniemy od przedstawienia szablonu, który może być użyty do dokumentowania


wyników analizy (patrz sekcja 5.5.1), następnie omówimy przypisywanie właściwych ról po-
szczególnym uczestnikom procesu analizy (patrz sekcja 5.5.2), po czym zajmiemy się problemem
komunikacji w tym procesie (patrz sekcja 5.5.3). Na zakończenie przedstawimy zadania
dla menedżera projektu, wynikające z iteratywnej i przyrostowej natury wymagań (patrz
sekcja 5.5.4).

5.5.1. Dokumentowanie analizy wymagań


Jak już pisaliśmy w poprzednim rozdziale, zbieranie wymagań i ich analiza dokumentowane
są w formie dokumentu RAD (Requirements Analysis Document), którego szkic struktury wi-
dzimy poniżej (szczegóły tej struktury opisane są w sekcji 4.5.3). Początek dokumentu, aż do
punktu 3.4.2, sporządzany jest na etapie zbierania wymagań. Na etapie analizy jest zwykle
przeredagowywany wskutek odkrycia niejasności lub nowych elementów funkcjonalnych,
główny jednak wysiłek skoncentrowany jest wówczas na punktach 3.4.3 i 3.4.4 opisujących
analityczny model obiektowy.

Dokument analizy wymagań


1. Wprowadzenie
2. System obecnie użytkowany
3. System proponowany
3.1. Streszczenie
3.2. Wymagania funkcyjne
3.3. Wymagania pozafunkcyjne
3.4. Modele systemu
3.4.1. Scenariusze
3.4.2. Model przypadków użycia
3.4.3. Model obiektowy
3.4.3.1. Słownik danych
3.4.3.2. Diagramy klas
3.4.4. Model dynamiczny
3.4.5. Interfejs użytkownika — ścieżki nawigacyjne i makiety ekranów
4. Słownik

W punkcie 3.4.3 dokumentu RAD opisane są szczegółowo wszystkie obiekty, ich atry-
buty i operacje (na diagramach sekwencji). Każdy obiekt opisywany jest w postaci tekstowej,
natomiast relacje między obiektami uwidaczniane są na diagramach klas.
W punkcie 3.4.4 udokumentowane jest zachowanie obiektów modelu w kategoriach dia-
gramów stanów i diagramów sekwencji. Jakkolwiek informacja ta jest redundantna z modelem
przypadków użycia, modele dynamiczne umożliwiają bardziej precyzyjne opisywanie skom-
plikowanych zachowań, między innymi przypadków użycia angażujących wielu aktorów.
4.5. Zarządzaniez b i e r a n i e mwymagań 239

Dokument RAD w momencie opublikowania otrzymuje status dokumentu bazowego


(„linii bazowej") i odtąd podlega kontrolowaniu w ramach zarządzania konfiguracją. Historia
zmian w dokumencie powinna dla każdej zmiany określać jej opis, datę oraz autora odpowie-
dzialnego za jej wprowadzenie.

5.5.2. Przydzielanie odpowiedzialności


Analiza wymagań angażuje być może znaczną liczbę uczestników. Użytkownicy dostarczają
wiedzę z zakresu dziedziny aplikacyjnej, klient zapewnia fundusze na realizację projektu i ko-
ordynuje poczynania użytkowników, analitycy formalizują zebraną wiedzę, menedżer projektu
koordynuje działania po stronie programistów. W realizację złożonego systemu uwikłanych
jest zwykle wielu użytkowników, analityków i programistów, co stwarza niebagatelne wyzwa-
nia dotyczące integrowania jednostkowych wysiłków i zapewnienia sprawnej komunikacji
między uczestnikami. Wyzwaniom tym można skutecznie stawiać czoła, przypisując poszcze-
gólnym uczestnikom dobrze zdefiniowane role i zakresy odpowiedzialności. Istnieją trzy
główne typy wspomnianych ról: generacyjna, integracyjna i weryfikacyjna.

• Użytkownik jest ekspertem z dziedziny aplikacyjnej, generującym informację na temat


dotychczasowego systemu, środowiska nowego systemu i zadań, jakie przyszły sys-
tem ma spełniać. Każdy użytkownik odpowiada jednemu aktorowi lub kilku aktorom
(w scenariuszach i przypadkach użycia).
• Klient to rola integracyjna, definiująca zakres funkcjonalny systemu w oparciu o po-
trzeby użytkowników. Różni użytkownicy mogą prezentować odmienne spojrzenie
na przyszły system, jako że korzystać będą z różnych jego części (jak Fi el dOffi cer
i Dispatcher), bądź też mają inne opinie i oczekiwania dotyczące systemu. Klient
służy jako integrator informacji z dziedziny aplikacyjnej i rozwiązuje niespójności
w oczekiwaniach poszczególnych użytkowników.
• Analityk to ekspert z dziedziny aplikacyjnej, tworzący modele aktualnego systemu
i generujący informację na temat przyszłego systemu. Każdy analityk jest począt-
kowo odpowiedzialny za uszczegółowienie jednego lub wielu przypadków użycia;
dysponując kompletem szczegółowych przypadków użycia, analityk identyfikuje po-
szczególne obiekty, ich atrybuty i skojarzenia między nimi; stosuje przy tym techniki
opisane w sekcji 5.4. Analitykiem jest zwykle programista dysponujący rozległą wiedzą
z zakresu dziedziny aplikacyjnej.
• Architekt, jako przedstawiciel roli integracyjnej, dokonuje unifikacji modelu przy-
padków użycia i modelu obiektowego, z perspektywy przyszłego systemu. Poszcze-
gólni analitycy mogą prezentować różne style modelowania i różne punkty spojrzenia
na te części systemu, za które sami nie są odpowiedzialni. Mimo iż analitycy często
pracują razem i wynikające z powyższego problemy rozwiązują we własnym zakresie,
rola architekta jest niezbędna w celu zachowania jednolitej filozofii systemu i ziden-
tyfikowania ewentualnych braków w specyfikacji wymagań.
• Redaktor dokumentacji odpowiedzialny jest za niskopoziomową integrację doku-
mentu, a także za jego ogólny format i użyteczny indeks.
240 Rozdział 4. •Zbieraniewymagań

• Menedżer konfiguracji odpowiada za utrzymywanie historii poprawek wprowa-


dzanych do dokumentu oraz za informacje wiążące dokument RAD z innymi do-
kumentami (takimi jak dokument projektu systemu opisywany w rozdziale 6.
„Projektowanie systemu — dekompozycja na podsystemy").
• Weryfikator dokonuje przeglądu dokumentu RAD pod kątem jego poprawności,
kompletności, spójności i klarowności. Użytkownicy, klient, programiści i inni uczest-
nicy projektu mogą wcielać się w rolę weryfikatorów — ci, którzy jeszcze nie są
czynnie zaangażowani w projekt, doskonale nadają się na weryfikatorów, prezentują
bowiem świeże spojrzenie zapewniające lepszą identyfikację niejasności i wielo-
znaczności.

Rozmiar przyszłego systemu determinuje liczbę różnych użytkowników i analityków


uczestniczących w zbieraniu wymagań i ich modelowaniu. Konieczne jest w związku z tym
istnienie roli integracyjnej zarówno po stronie klienta, jak i po stronie programistów. Osta-
tecznie bowiem wymagania dotyczące przyszłego systemu — jak wielkim by nie był — muszą
być zrozumiałe dla pojedynczej osoby posiadającej niezbędną do tego wiedzę z dziedziny
aplikacyjnej.

5.5.3. Komunikacja w związku z analizą wymagań


Zadanie sprawnego komunikowania informacji jest jednym z najtrudniejszych wyzwań w pro-
cesie zbierania wymagań i ich analizy. Wyzwanie to kreowane jest przez rozmaite czynniki.
Oto niektóre z nich.

• Zróżnicowanie uczestników pod względem posiadanej wiedzy i doświadczenia. Użyt-


kownicy, klient i programiści dysponują wiedzą i doświadczeniem z różnych dziedzin
i przy opisywaniu tych samych koncepcji posługują się odmienną terminologią.
• Zróżnicowane oczekiwania uczestników. Cele, jakie stawiają sobie użytkownicy,
klient i menedżer projektu są zróżnicowane: użytkownicy oczekują systemu, który
usprawni ich pracę, bez wpływu na bieżącą pozycję (wdrożenie nowego systemu
może się wiązać ze zmianami kadrowymi); klient chciałby uzyskać jak najwięcej
z zainwestowanych zasobów, menedżer dąży do dostarczenia systemu w terminie.
Zróżnicowanie interesów (również osobistych) może prowadzić do zniechęcenia
w kwestii dzielenia się informacjami i raportowania problemów niezwłocznie, gdy
tylko się pojawią.
• Nowo utworzone zespoły. Zbieranie wymagań jest pierwszą fazą nowego projektu,
który przekłada się często na nowych uczestników, nowe zespoły i przypisywanie
nowych ról, a w konsekwencji — na pewien okres niestabilności, w czasie którego
członkowie każdego zespołu muszą nauczyć się pracować razem.
• Ewoluujący system. Gdy nowy system tworzony jest „od zera", związane z nim kon-
cepcje i terminologia są czynnikami płynnymi w trakcie zbierania i analizowania
wymagań, a często także w fazie projektowania systemu. Używany dziś termin może
jutro mieć inne znaczenie.
4.5. Zarządzaniez b i e r a n i e mwymagań 241

Żadna metodologia i żadne mechanizmy nie są zdolne do rozwiązywania problemów


wynikających z wewnętrznej polityki firmy oraz z ukrywania informacji; sprzeczne cele i współ-
zawodnictwo są nieodłącznymi elementami każdego projektu. Kilka prostych wskazówek
może jednak przyczynić się przynajmniej do złagodzenia konfliktów w kwestii spojrzenia na
nowy system.

• Wyraźne określenie granic. Definiowanie ról, opisane w sekcji 5.5.2, jest częścią tego
procesu, który obejmuje także zdefiniowanie granic prywatności informacji: przykła-
dowo każdy zespół ma do dyspozycji własne, wewnętrzne forum dyskusyjne, niewi-
doczne dla klienta, z którym dyskusja odbywa się w ramach zupełnie innego forum
(opisywaliśmy to w rozdziale 3. „Organizacja projektu i komunikacja"). Na zasadzie
wzajemności — programiści nie powinni ingerować w wewnętrzną politykę klienta
i użytkowników.
• Wyraźne zdefiniowanie celów oraz kryteriów powodzenia. Wspólne definiowanie
czytelnych, mierzalnych i weryfikowalnych celów oraz kryteriów powodzenia przy-
czynia się do łagodzenia konfliktów między programistami a klientem. Czytelne
zdefiniowanie weryfikowalnego celu nie jest zadaniem banalnym, gdy weźmie się pod
uwagę tendencję do pozostawiania definicji celów w stanie mało precyzyjnym. Cele
i kryteria powodzenia powinny być opisane w punkcie 1.3 dokumentu RAD.
• Burza mózgów. Szybkie proponowanie rozmaitych rozwiązań podczas zebrania
wszystkich uczestników projektu w jednym pomieszczeniu sprzyja przełamywaniu
barier komunikacyjnych. Podobny efekt dać mogą przeglądy produktów docelowych
dokonywane wspólnie przez klienta i programistów w czasie tej samej sesji.

Burza mózgów — i generalnie każde kooperatywne opracowywanie wymagań — może


prowadzić do definiowania ad hoc rozmaitych konwencji komunikacyjnych i notacyjnych:
tablice, szkice interfejsu użytkownika, wysokopoziomowe diagramy przepływu informacji i tak
dalej często pojawiają się spontanicznie. W miarę jak objętość informacji dotyczącej dziedziny
aplikacyjnej i nowego systemu staje się coraz większa, krytycznego znaczenia nabiera stoso-
wanie precyzyjnej i strukturalnej notacji. Stosując język UML, programiści wykorzystują przy-
padki użycia i scenariusze, do komunikowania się z klientem i użytkownikami, oraz diagramy
klas, diagramy sekwencji i diagramy stanów do komunikowania się między sobą (patrz sekcje
4.4 i 5.4). Ponadto najnowsza wersja wymagań powinna być dostępna dla wszystkich uczest-
ników — utrzymywanie aktualnej wersji online dokumentu RAD wraz z historią jego rewizji
ułatwia terminowe dostarczanie informacji w ramach projektu.

5.5.4. Iteracje modelu analitycznego


Analizowanie wymagań to proces iteratywny i przyrostowy, prowadzony często równolegle
z innymi aktywnościami, między innymi projektowaniem systemu i jego implementacją.
Zwróćmy uwagę, że niekontrolowane modyfikowanie i poszerzanie modelu analitycznego
rychło prowadzi do chaosu, szczególnie wtedy, gdy ingeruje w ów model duża liczba uczestni-
ków. Od momentu zatem, gdy dokument RAD uzyska status „linii bazowej", kolejne iteracje
modelu powinny być starannie dokumentowane. Aktywności związane ze zbieraniem i anali-
zowaniem wymagań mogą być postrzegane jako sekwencje kroków zmierzających do wypra-
cowania stabilnego modelu analitycznego.
242 Rozdziat 5. • Analiza wymagań

Burza mózgów

Burza mózgów, towarzysząca zbieraniu wymagań, jest najczęściej pierwszą aktywno-


ścią w projekcie. W trakcie burzy mózgów wszelkie koncepcje, a także terminologia służąca
do ich formułowania, ulegają nieustannym zmianom. Celem tego procesu jest wygenerowanie
jak największej liczby koncepcji bez potrzeby jakiegokolwiek ich organizowania. Iteracje są tu
bardzo krótkie i mocno zróżnicowane.

Ustalanie

Gdy tylko klient i programiści porozumieją się co do zasadniczych koncepcji, zdefiniują


granice systemu i uzgodnią standardową terminologię, zaczyna się proces ustalania: funkcjo-
nalność organizowana jest w grupy przypadków użycia wraz z odpowiadającymi im ele-
mentami interfejsu. Poszczególne grupy elementów funkcjonalnych przyporządkowywane są
różnym zespołom, które odpowiedzialne są za uszczegółowienie poszczególnych przypadków
użycia. Na tym etapie iteracje także są krótkie, lecz już raczej zlokalizowane.

Dojrzewanie

Zmiany wysokopoziomowe wciąż są możliwe, lecz już trudniejsze do wprowadzania,


powinny więc być dokonywane w sposób szczególnie przemyślany. Każdy zespół odpowie-
dzialny jest za przypadki użycia i modele obiektowe związane z funkcjonalnością, która została
mu powierzona. Zespół architektoniczny, jako zespół międzyfunkcyjny, złożony z reprezen-
tantów poszczególnych zespołów funkcyjnych, odpowiedzialny jest za integrowanie wymagań
(między innymi za przyporządkowywanie unikalnych nazw).
Gdy specyfikacja wymagań zostanie przyjęta (podpisana) przez klienta, modyfikacje
modelu analitycznego powinny ograniczać się tylko do usuwania błędów i uzupełniania prze-
oczeń. Programiści, szczególnie ci z zespołu architektonicznego, powinni dbać o to, by nie
została naruszona spójność modelu. Model wymagań powinien być poddany zarządzaniu
konfiguracją: wszelkie zmiany w jego obrębie powinny być natychmiast odzwierciedlane w ist-
niejących modelach projektowych. Iteracje na tym etapie są dłuższe i zwykle zlokalizowane.
Zestaw cech i funkcji przyszłego systemu rozrasta się z czasem, każda zmiana w tym ze-
stawie stanowi zagrożenie dla integralności systemu. Wprowadzanie zmian w późnych etapach
projektu dokonywane jest zwykle w warunkach niepełnej informacji: nie wszystkie zależności
między poszczególnymi elementami funkcjonalnymi są widoczne, wiele założeń przyjmowa-
nych jest domyślnie i zapominanych w trakcie urzeczywistniania zmian. Często zmiany takie
związane są z problemem, co do którego istnieje silna presja na implementację: badanie ich
konsekwencji jest wówczas tylko powierzchowne. Zawsze w talach wypadkach należy zadać
sobie następujące pytania:

• Czy wprowadzenia zmian żąda klient?


• Czy są niezbędne dla systemu, czy też mają jedynie charakter ozdobników?
« Czy może powinny być zrealizowane raczej w postaci odrębnego programu niż
jako element systemu bazowego?
4.5. Zarządzaniez b i e r a n i e mwymagań 243

• Czy dotyczą zasadniczej funkcjonalności systemu, czy też jego opcjonalnych funkcji?
• Jakie są ich konsekwencje dla spójności i niezawodności systemu? Dla interfejsu
systemu?

Gdy wspomniane zmiany okazują się uzasadnione, klient i programiści definiują ich
zakres, ich oczekiwany efekt i przystępują do modyfikowania modelu analitycznego. Gdy
model ten jest kompletny, wprowadzanie do niego nowych elementów funkcjonalnych jest
łatwiejsze, choć trudniejsza staje się wtedy ich implementacja.

5.5.5. Uzgodnienie modelu analitycznego z klientem


Podpis złożony przez klienta pod dokumentem RAD oznacza akceptację modelu analitycznego,
czyli porozumienie między klientem a programistami co do funkcji i cech przyszłego systemu.
Dodatkowo klient i programiści uzgadniają między sobą następujące szczegóły:

• listę priorytetów,
• proces rewizji dokumentu,
• listę kryteriów zaakceptowania lub odrzucenia gotowego systemu,
• harmonogram i budżet.

Zróżnicowanie funkcji systemu pod względem ważności (priorytetów) pozwala progra-


mistom lepiej rozumieć oczekiwania klienta, czyli między innymi odróżniać kluczowe cechy
systemu od przysłowiowych wodotrysków. Umożliwia ono również programistom lepsze za-
planowanie dostarczania poszczególnych elementów systemu: najpierw zrealizowane będą
jego kluczowe funkcje, a dopiero po ich zweryfikowaniu pozostałe. A jeśli nawet system do-
starczony ma być jednorazowo w postaci kompletnego, monolitycznego pakietu, zróżnicowa-
nie priorytetów jego funkcji umożliwia programistom lepsze zrozumienie wyobrażeń klienta
o produkcie. Poniżej przedstawiamy przykładowy schemat priorytetów tego rodzaju.

Każda funkcja systemu powinna być zakwalifikowana


w kategoriach następujących priorytetów:
® wysoki priorytet — funkcje tej grupy muszą być z powodzeniem zaprezentowane podczas
testów akceptacyjnych u klienta,

o pośredni priorytet — funkcje tej grupy muszą zostać wzięte pod uwagę w czasie projektowania
systemu i projektowania obiektów, ich prezentacja przewidywana jest w trakcie drugiej iteracji
tworzenia systemu,

° niski priorytet — funkcje tej grupy stanowią ilustrację możliwości zarządzania systemu w dłuższej
perspektywie.

Podpisane przez klienta wymagania uzyskują status linii bazowej i mogą zostać wyko-
rzystane do dokładniejszego oszacowania kosztów projektu. Oczywiście, nie oznacza to końca
zmian w samych wymaganiach, lecz odtąd każda z tych zmian dokonywana będzie w ramach
formalnego procesu rewizji. Nieuchronność tych zmian wynika z błędów, przeoczeń, zmian
244 Rozdział 4. •Zbieraniewymagań

w środowisku operacyjnym, zmian w zakresie dziedziny aplikacyjnej lub zmian w dostępnych


technologiach. Zdefiniowanie a priori szczegółów wspomnianego procesu rewizji ma na celu
usprawnienie komunikacji w projekcie i zapobieżenie przykrym niespodziankom w później-
szych jego etapach. Ów proces nie musi mieć wcale charakteru biurokratycznego, niekiedy
wystarczy wyznaczenie jednej osoby odpowiedzialnej za odebranie żądania od klienta, zapro-
jektowanie stosownej zmiany i nadzór nad jej implementacją. Na rysunku 5.18 przedstawiony
jest jednak przypadek bardziej skomplikowany, kiedy to projekt zmian musi zostać zatwier-
dzony przez klienta przed ich zaimplementowaniem. Tak czy inaczej żądanie, by aktualna
postać wymagań nie została zamrożona, lecz tylko opatrzona statusem linii bazowej, po-
dyktowane jest realiami realizacji projektu.

Rysunek 5.18. Przykład procesu rewizyjnego


5.6. Analiza przypadku — system AREIMA 245

Lista kryteriów akceptacyjnych weryfikowana jest jeszcze przed podpisaniem dokumentu.


Zbieranie wymagań i ich analiza wyjaśniają wiele aspektów systemu, włącznie z wymaganiami
pozafunkcyjnymi i względną ważnością poszczególnych jego cech. Formułując ponownie
kryteria akceptacyjne, klient upewnia się, że programiści świadomi są wszystkich zmian, ja-
kie dokonały się w jego oczekiwaniach.
Budżet i harmonogram podlegają ponownej analizie po tym, jak model analityczny okaże
się stabilny. W rozdziale 14. „Zarządzanie projektem" opiszemy zagadnienia związane z szaco-
waniem kosztów realizacji systemu.
Niezależnie od zewnętrznej formy, czyli od tego, czy opisane uzgodnienia mają postać
formalnego dokumentu, czy też postać procedury wynikającej z podpisanego wcześniej kon-
traktu, stanowią one kamień milowy w realizacji projektu, reprezentują bowiem zbieżność
klienta i programistów w kwestii jednolitego zbioru definicji funkcjonalnych systemu i jedno-
litego zbioru oczekiwań klienta. Akceptacja dokumentu RAD przez klienta ma znaczenie
większe niż akceptacja pozostałych dokumentów, bo to właśnie model analityczny deter-
minuje znakomitą większość aktywności w realizacji projektu.

5.6. Analiza przypadku — system ARENA


W tej sekcji pokażemy, jak opisywane w tym rozdziale koncepcje i metody zastosować można
w systemie ARENA. Rozpoczniemy od modelu przypadków użycia i słownika, opracowanych
w poprzednim rozdziale. Zidentyfikujemy uczestniczące w tych przypadkach użycia obiekty
encji, brzegowe i sterujące, po czym wzbogacimy model analityczny, definiując atrybuty tych
obiektów i skojarzenia między nimi. Na zakończenie skonsolidujemy model obiektowy, wy-
korzystując relację dziedziczenia.
W sekcji tej opierać się będziemy głównie na przypadku użycia AnnounceTournament.

5.6.1. Identyfikacja obiektów encji


Obiekty encji reprezentują koncepcje z dziedziny aplikacyjnej zarządzane przez system. Jako
punktu wyjścia do identyfikowania obiektów tej kategorii użyjemy słownika, który powstał
podczas zbierania wymagań, po czym odwołamy się do heurystyk Abbotta (przedstawionych
w tabeli 5.1). Początkowo skupimy się tylko na rzeczownikach odzwierciedlających koncepcje
dziedziny aplikacyjnej. W tabeli 5.8 widoczny jest opis przypadku użycia AnnounceTournament,
w którym to opisie wyróżniono czcionką pogrubioną pierwsze wystąpienie każdego ze wspo-
mnianych rzeczowników.
Zwróćmy uwagę, że zidentyfikowaliśmy obiekty odpowiadające aktorom w modelu przy-
padków użycia. Aktor to koncepcja z zakresu dziedziny aplikacyjnej, mająca związek z syste-
mem (w sensie na przykład kontroli dostępu, autorstwa czy zakresu odpowiedzialności).
W systemie ARENA każdy zarejestrowany kapitan ligi (LeagueOwner) reprezentowany jest
przez obiekt przechowujący specyficzne dla tego kapitana informacje: adres e-maił, listę po-
siadanych lig i tak dalej.
Zauważmy także, iż nie wszystkie frazy rzeczowników odnoszą się bezpośrednio do klas.
Przykładowo „nazwa turnieju" identyfikuje atrybut name, reprezentujący nazwę turnieju (który
z kolei reprezentowany jest przez obiekt klasy Tournament). Podobnie „lista reklamodawców"
246 Rozdziat 5. • Analiza wymagań

Tabela 5.8. Zastosowanie heurystyk A b b o t t a do identyfikacji obiektów encji w przypadku użycia


AnnounceTournaraent. Pierwsze wystąpienie każdego z rzeczowników wyróżnione jest pogrubioną
czcionką

Nazwa AnnounceTournament

Przepływ zdarzeń 1. LeagueOwner wysyła żądanie zorganizowania nowego turnieju.


2. System sprawdza, czy LeagueOwner nie wykorzystywał już
przysługującego mu limitu liczby turniejów w lidze lub na arenie;
jeśli mieści się w limicie, system wysyła mu formularz do wypełnienia.
3. LeagueOwner wpisuje w formularzu swą nazwę, początkową i końcową datę
zgłaszania graczy do turnieju, początkową i końcową datę rozgrywania meczów
w ramach turnieju i maksymalną liczbę graczy biorących udział w turnieju.
4. System wysyła d o kapitana ligi (LeagueOwner) zapytanie
o ewentualny sponsoring wyłączny turnieju i w przypadku
decyzji o takim sponsoringu wyświetla mu listę reklamodawców
(Adverti s e r ) wyrażających chęć takiego sponsoringu.
5. Jeśli LeagueOwner zdecyduje o poszukiwaniu konkretnego sponsora
wyłącznego, wybiera kilka pozycji z proponowanej listy.
6. System powiadamia wybranych sponsorów o nadchodzącym
turnieju i wysokości ryczałtowej opłaty {flatfee) wyłącznego
sponsoringu.
7. System przekazuje kapitanowi ligi odpowiedzi otrzymane
od zainteresowanych sponsorów.
8. Jeżeli istnieją zainteresowani potencjalni sponsorzy, LeagueOwner
wybiera jednego z nich.
9. System rejestruje dokonany wybór nazwy wyłącznego sponsora
i obciąża jego k o n t o opłatą sponsoringową. Od tej chwili
w reklamach podczas turnieju pojawiać się będą banery tylko
tego jednego sponsora.
10. Jeżeli nie wyłoniono wyłącznego sponsora — z braku chętnych
lub rezygnacji kapitana ligi z wyboru — w czasie turnieju
wyświetlane będą banery wybierane losowo spośród materiałów
wszystkich zarejestrowanych sponsorów, których konta
obciążone zostaną wynikającymi stąd opłatami
proporcjonalnymi.
11. Gdy załatwione zostaną kwestie sponsoringu, system wyświetla
listę graczy, kibiców i reklamodawców zainteresowanych nowym
turniejem.
12. LeagueOwner wybiera osoby, które zostaną powiadomione o nowym turnieju.
13. System tworzy stronę główną nowego turnieju stanowiącą punkt
startowy dla graczy zamierzających zgłosić swój udział i kibiców
zainteresowanych oglądaniem meczów.
14. Gdy nadejdzie data, od której przyjmowane są zgłoszenia, system
powiadamia o tym wszystkich zainteresowanych użytkowników,
przesyłając im link do strony głównej turnieju. Aż do ustalonego
m o m e n t u gracze mogą zgłaszać swój udział (przypadek użycia
Apply ForTournament).
5.6. Analiza przypadku — system AREIMA 247

to identyfikacja skojarzenia między klasami League (obiekt tej klasy reprezentuje


ligę) i Adverti ser (na tę ldasę składają się obiekty reprezentujące reklamodawców). Aby ułatwić
rozróżnienie między frazami identyfikującymi (odpowiednio) obiekty, atrybuty i skojarzenia,
posłużymy się kilkoma dodatkowymi heurystykami:

• Atrybuty są właściwościami. Atrybut reprezentuje pojedynczą właściwość obiektu,


wycinek jego informacji i jako taki jest z natury niekompletny: nazwa reklamodawcy
to tylko jeden z opisujących reklamodawcę elementów, oprócz między innymi iden-
tyfikacji jego konta, typu prezentowanych banerów i tym podobnych, które repre-
zentowane są przez inne atrybuty i skojarzenia.
• Atrybuty są wielkościami prostych typów. Atrybut może mieć postać liczby (będącej
na przykład maksymalną dopuszczalną liczbą turniejów), łańcucha znaków (przed-
stawiającego przykładowo nazwę reklamodawcy) lub daty (na przykład daty rozpo-
częcia i zakończenia turnieju). Właściwości, takie jak adres, numer ubezpieczenia czy
numer rejestracyjny samochodu, też są zwykle uważane za typy proste (kwalifikują
się więc na atrybuty), ponieważ odnoszą się do prostych, niepodzielnych koncepcji.
Koncepcje bardziej złożone to kandydatury na reprezentowanie w postaci odrębnych
obiektów, powiązanych z obiektem macierzystym za pomocą skojarzeń: przykładowo
konto (Account) danego reklamodawcy (Adverti ser) to obiekt zawierający między
innymi takie informacje jak aktualny bilans, historia transakcji czy limit kredytowy.
• Rzeczowniki odnoszące się do kolekcji reprezentują skojarzenia, często z automa-
tycznie określonymi krotnościami. Listy, grupy, tabele czy zbiory reprezentowane są
właśnie przez skojarzenia; i tak na przykład system ARENA wyświetla na żądanie ka-
pitana ligi listę reklamodawców, potencjalnie zainteresowanych wyłącznym sponso-
ringiem; koncepcja ta reprezentowana jest przez skojarzenie między klasami Arena
i Adverti ser. Często klasa uwikłana w skojarzenie uczestniczy w nim na zasadzie
implikacji. Przykładowo, gdy rozwiązane zostaną kwestie sponsoringu, system ARENA
prezentuje kapitanowi ligi, organizującemu turniej, listy przedstawiające grupy graczy
(Player), kibiców (Spectator) i reklamodawców (Advertiser). Identyfikujemy
w związku z tym nową klasę InterestGroup, reprezentującą grupę użytkowników
zainteresowanych nowych zdarzeniem dotyczącym ligi lub gry. Stosownie do słowa
„lista", obiekt klasy Arena skojarzony jest z obiektami klasy InterestGroup repre-
zentującymi wszystkie zdefiniowane grupy zainteresowań. Z kolei każda grupa zainte-
resowań jest grupą użytkowników określonej kategorii — graczy, kibiców i rekla-
modawców — stąd skojarzenie klasy InterestGroup z klasami Player, Spectator
i Advertiser. Każda grupa zainteresowań powiązana jest również odpowiednim
skojarzeniem z przedmiotem swego zainteresowania, stąd kolejne skojarzenie między
klasą InterestGroup a k l a s a m i League i Game.

W tabeli 5.9 prezentujemy listę obiektów encji, ich atrybutów oraz skojarzeń między
ich klasami zidentyfikowanych dotychczas na podstawie opisu przypadku użycia Announce
"-•Tournament. Dokonaliśmy przyporządkowania rozpoznanych atrybutów i skojarzeń do
odpowiednich klas, zdefiniowaliśmy też nowe klasy. Każdą klasę opatrzyliśmy także opisem
słownym, głównie z dwóch powodów. Po pierwsze, sama nazwa klasy nie jest na tyle specyficzna,
by jednoznacznie wskazywać wszystkim uczestnikom koncepcję, którą klasa ta reprezentuje
248 Rozdział 4. •Zbieraniewymagań

Tabela 5.9. Obiekty encji uczestniczące w przypadku użycia AnnounceTournament zidentyfikowane


na podstawie fraz rzeczownikowych. Znaki zapytania (?) oznaczają wątpliwości prowadzące do pytań
przedstawionych w ramce poniżej

Obiekt encji Atrybuty i skojarzenia Definicja


Account • bilans Konto reklamodawcy (Adverti s e r )

historia transakcji zawierające informację o bieżącym bilansie
• historia płatności reklamodawcy, historii zmian i płatnościach.

Adverti s e r • nazwa Reklamodawca, aktor zainteresowany


• ligi zainteresowane wyświetlaniem swych banerów reklamowych
wyłącznym s p o n s o r i n g i e m w trakcie trwania meczów (Match).

tego reklamodawcy (?)

sponsorowane turnieje
konto
Advertisement • skojarzona gra (?) Obrazy d o s t a r c z o n e przez reklamodawcę
(Adverti s e r ) wyświetlane w czasie trwania
meczów.
Arena • maksymalna liczba turniejów Instancja systemu ARENA.
• zryczałtowana opłata
• za wyłączny sponsoring (?)
• ligi (implikowany)
grupy zainteresowań
(implikowany)

Game Gra, współzawodnictwo między graczami,


prowadzone w oparciu o określony zestaw reguł.
W systemie ARENA klasa Game odpowiedzialna
jest za w y m u s z a n i e w s p o m n i a n y c h reguł
(w kodzie programu), śledzenie postępów
poszczególnych graczy (PI a y e r ) i wybór
zwycięzcy.

InterestGroup • lista graczy (PI ayer), Lista u ż y t k o w n i k ó w systemu ARENA


kibiców ( S p e c t a t o r ) o określonym zainteresowaniu (którego
0 i reklamodawców p r z e d m i o t e m jest gra lub liga). Klasa
(Adverti s e r ) I n t e r e s t G r o u p wykorzystywana jest także
gry i ligi będące do reprezentowania list mailingowych,
przedmiotem służących powiadamianiu potencjalnych
zainteresowania grupy aktorów o nowych zdarzeniach.
(implikowany)
League o maksymalna liczba turniejów Liga, wspólnota, której celem jest rozgrywanie
0 gra t u r n i e j ó w (Tournament). Liga skojarzona
jest z a r ó w n o z określoną grą (Game),
jak i p r e d e f i n i o w a n y m stylem t u r n i e j u
(TournamentStyl e). Gracze zarejestrowani
w lidze gromadzą punkty zgodnie z regułami
ExpertRating określonymi dla tej ligi.
5.6. Analiza przypadku — system AREIMA 249

Tabela 5.9. Obiekty encji uczestniczące w przypadku użycia AnnounceTournament zidentyfikowane


na podstawie fraz rzeczownikowych. Znaki zapytania (?) oznaczają wątpliwości prowadzące do pytań
przedstawionych w ramce poniżej — ciąg dalszy

Obiekt encji Atrybuty i skojarzenia Definicja


LeagueOwner • nazwa (implikowany) Aktor zakładający ligę (League)
i odpowiedzialny za organizowanie
turniejów (Tournament) w j e j ramach.

Match • turnieje Mecz, konkurs między dwoma (lub większą



gracze iiczbą) graczami ( P l a y e r ) p r o w a d z o n y
w oparciu o reguły danej gry (Game). W wyniku
meczu bądź to wyłoniony zostaje jeden
zwycięzca (pozostali gracze uczestniczący
w meczu uważani są za przegranych), bądź
też u s t a n o w i o n y remis (nie ma wówczas
zwycięzcy ani przegranych). Niektóre style
turnieju (TournamentStyl e) mogą wykluczać
remis.

Player • nazwa (implikowany) Gracz.

Tournament • nazwa Turniej, seria meczów rozgrywanych między



data rozpoczęcia zgłaszania graczami (PI ayer) z określonej grupy. Turniej

kończy się wyłonieniem jednego zwycięzcy.
data zakończenia zgłaszania
Gracze uczestniczący w turnieju gromadzą
• data rozpoczęcia rozgrywek
• punkty. H a r m o n o g r a m meczów w turnieju
data zakończenia rozgrywek ustalany jest przez kapitana ligi (LeagueOwner)
a maksymalna liczba graczy organizującego ten turniej.

wyłączny sponsor

— wyrazy „mecz" (Match) czy „gra" (Game) używane są w różnych kontekstach w znaczeniu
potocznym; w systemie ARENA reprezentują one jednak ściśle zdeterminowane koncepcje: i tak
„gra" to zestaw reguł, których zachowanie wymuszane jest przez odpowiedni fragment opro-
gramowania, a „mecz" to rozgrywka między graczami (PI ayer). Po drugie, obiekty zidentyfi-
kowane na etapie analizy odpowiadają także terminom zawartym w słowniku, którego tworze-
nie rozpoczęliśmy na etapie zbierania wymagań; uczestnicy wykorzystują ów słownik w celu
rozwiązywania niejasności i ustanowienia standardowej terminologii, zatem krótka definicja
znaczenia każdej z klas jest znakomitym środkiem do zapobiegania wieloznacznościom i nie-
porozumieniom. Odłożenie „na później" tych definicji stwarzałoby ryzyko utraty informacji,
prowadzącej w konsekwencji do ich niekompletności.
Identyfikowanie obiektów encji i ich atrybutów rzadko jest oczywiste i zwykle rodzi serię
dodatkowych pytań pod adresem klienta. Przykładowo, gdy rozpoznajemy implikowane atry-
buty i skojarzenia, powinniśmy się solidnie zastanowić wraz z klientem, czy intuicja nie zwiodła
nas na manowce — może się bowiem okazać, że strony skojarzenia nie są określone jedno-
znacznie. Poniżej widzimy przykładowy zestaw pytań, związanych z obiektami encji uczestni-
czącymi w przypadku użycia AnnounceTournament.
250 Rozdział 4. •Zbieraniewymagań

Pytania dla klienta systemu ARENA


® Jaka informacja powinna być przechowywana w koncie reklamodawcy? Czy na przykład
powinna ona obejmować kompletną kronikę wszystkich wyświetlonych banerów?

• Czy reklamodawcy mogą wyrażać chęć wyłącznego sponsorowania wybranych lig, czy jedynie
chęć uczestniczenia w sponsoringu na arenie?
• Czy reklamodawcy p o w i n n i przyporządkowywać swe b a n e r y do określonych gier,
w celu ich trafniejszego losowego wyboru przez system, w sytuacji gdy nie ma
wyłącznego sponsora?

• Czy zryczałtowana opłata za wyłączny sponsoring powinna być stała w danej instancji
systemu, czy też zróżnicowana dla poszczególnych lig i (lub) turniejów?

5.6.2. Identyfikacja obiektów brzegowych


Obiekty brzegowe reprezentują interfejs między systemem a aktorami; identyfikowane są na
podstawie przypadków użycia, a ich komplet może być uważany za dobre przybliżenie inter-
fejsu użytkownika przyszłego systemu. Nie powinny się jednak odnosić do jakichkolwiek wi-
zualnych aspektów owego interfejsu, bo ten rodzaj informacji lepiej reprezentowany jest przez
rozmaite makiety; mogą za to reprezentować koncepcje kompozycyjne interfejsu — okna,
formularze czy nawet sprzęt, na którym użytkownik będzie się komunikował z funkcjami
systemu za pośrednictwem tego właśnie interfejsu.
Stosując heurystyki Abbotta, nie rozpoznamy — niestety — zbyt wielu obiektów brze-
gowych, gdyż w początkowych wersjach przypadków użycia rzadko są one wymienione explicite.
Zamiast tego przeanalizujemy przypadek użycia AnnounceTournament z tabeli 5.8 i zidentyfi-
kujemy te jego miejsca, w których następuje wymiana informacji między aktorami a syste-
mem. Uwzględnimy zarówno formularze, za pomocą których aktorzy dostarczają informacje
systemowi (na przykład formularz, przy użyciu którego kapitan ligi (LeagueOwner) organizuje
nowy turniej (Tournament)), jak i powiadomienia, jakie system przesyła aktorom (na przykład
informację, jaką otrzymuje reklamodawca (Advertiser) zainteresowany sponsorowaniem
turnieju). W tabeli 5.10 widzimy definicje obiektów brzegowych przypadku użycia Announce
'-•Tournament, zaś w ramce poniżej widoczna jest kolejna porcja pytań dla klienta, związanych
z tymi obiektami.

Kolejne pytania dla klienta systemu ARENA


® Jak należy traktować sponsorów, którzy nie odpowiadają na powiadomienia?

• Jak można się zareklamować przy okazji nowego turnieju, jeśli brakuje grup
zainteresowanych tym turniejem?
® W jaki sposób użytkownicy powinni być powiadamiani o zdarzeniach (przez e-mail, telefon
komórkowy czy dedykowaną skrzynkę osobistą w systemie ARENA)?
5.6. Analiza przypadku — system AREIMA 251

Tabela 5.10. Obiekty brzegowe uczestniczące w przypadku użycia AnnounceTournament

Obiekt brzegowy Definicja


TournamentForm Formularz wykorzystywany przez kapitana ligi (LeagueOwner)
do określenia właściwości turnieju (Tournament), z związku
z organizowaniem nowych turniejów lub modyfikowaniem
istniejących.
RequestSponsorshipForm Formularz wykorzystywany przez kapitana ligi (LeagueOwner)
do poszukiwania sponsora wśród zainteresowanych reklamodawców
(Adverti ser).
SponsorshipRequest Powiadomienie otrzymane przez reklamodawcę (Adverti s e r )
zgłaszającego chęć sponsorowania turnieju lub ligi.
SponsorshipReply Powiadomienie otrzymane przez kapitana ligi (LeagueOwner),
informujące, czy dany reklamodawca (Adverti s e r )
zainteresowany jest wyłącznym sponsorowaniem turnieju.
S e l e c t E x c l u s i veSponsorForm Formularz wykorzystywany przez kapitana ligi (LeagueOwner)
do wybrania wyłącznego sponsora turnieju i zamknięcia tym
samym kwestii sponsoringu dla tego turnieju.
NotifylnterestGroupsForm Formularz wykorzystywany przez kapitana ligi (LeagueOwner)
do wysyłania powiadomień zainteresowanym użytkownikom.
InterestGroupNotice Powiadomienie o zorganizowaniu nowego turnieju (Tournament)
otrzymane przez zainteresowanych użytkowników.

Zauważmy, że AnnounceTournament jest przypadkiem użycia stosunkowo złożonym, anga-


żującym wielu aktorów; skutkuje to stosunkowo dużą liczbą obiektów brzegowych. W praktyce
większość spotykanych przypadków użycia zadowala się jednym obiektem brzegowym, za po-
mocą którego aktor inicjuje dany przypadek. Jednak każdy przypadek użycia musi posiadać
przynajmniej jeden obiekt brzegowy, nawet jeśli współdzielony jest on z innymi przypadkami.

5.6.3. Identyfikacja obiektów sterujących


Obiekty sterujące reprezentują koordynację między obiektami brzegowymi a obiektami encji.
W typowym przypadku użycia pojedynczy obiekt sterujący, tworzony zaraz na początku, przez
cały czas trwania przypadku gromadzi informację niezbędną do jego zamknięcia; po zakoń-
czeniu przypadku użycia wspomniany obiekt sterujący jest niszczony.
W przypadku użycia AnnounceTournament zidentyfikowaliśmy pojedynczy obiekt ste-
rujący — obiekt klasy AnnounceTournamentControl, odpowiedzialny za wysyłanie powiado-
mień do reklamodawców i kolekcjonowanie tych powiadomień, kontrolowanie dostępności
zasobów oraz powiadamianie zainteresowanych użytkowników o wybranych zdarzeniach. Za-
uważmy przy tym, że generalnie w tym samym przypadku użycia uczestniczyć może kilka
obiektów sterujących, jeśli na przykład trzeba koordynować alternatywny przepływ zdarzeń
czy współpracę kilku asynchronicznych stacji roboczych lub gdy zakończenie przypadku użycia
może być konsekwencją różnych informacji pochodzących z odmiennych źródeł.
252 Rozdział 4. •Zbieraniewymagań

5.6.4. Modelowanie interakcji między obiektami


Zidentyfikowaliśmy już pewną liczbę obiektów encji, brzegowych i sterujących uczestni-
czących w przypadku użycia AnnounceTournament, zdefiniowaliśmy także ich atrybuty oraz
skojarzenia między nimi. Teraz przedstawimy wspomniane obiekty na diagramie sekwencji,
by poprzez uwidocznienie interakcji zachodzących w ramach wspomnianego przypadku uży-
cia zidentyfikować dodatkowe atrybuty i skojarzenia.
W diagramie sekwencji poszczególne kolumny odpowiadają poszczególnym obiektem,
identyfikowanym w skrajnie górnym wierszu. Począwszy od skrajnej lewej kolumny, są to: ak-
tor inicjujący (LeagueOwner), obiekt brzegowy warunkujący inicjację (TournamentForm), główny
obiekt sterujący (AnnounceTournamentControl) i obiekty encji (Arena, League i Tournament).
Kolejne kolumny przeznaczone są dla pozostałych aktorów uczestniczących i odpowiednich
dla tych aktorów obiektów brzegowych. Ze względu na rozmiar wspomnianego diagramu
podzielimy go na trzy fragmenty widoczne na trzech kolejnych rysunkach: i tak rysunek 5.19
przedstawiać będzie interakcje prowadzące do zorganizowania nowego turnieju, rysunek 5.20
związany będzie z przepływem sterowania w związku z poszukiwaniem i wyborem wyłącz-
nego sponsora, zaś na rysunku 5.21 widoczne będą powiadamiania zainteresowanych grup
o wybranych zdarzeniach.
Diagram na rysunku 5.19 nie jest skomplikowany: LeagueOwner żąda utworzenia nowe-
go turnieju i określa jego parametry początkowe (nazwę i maksymalną liczbę graczy). Two-
rzony jest obiekt AnnounceTournamentControl i — o ile pozwala na to stan dostępnych zasobów
— obiekt encji Tournament reprezentujący nowy turniej.

Rysunek 5.19. Diagram sekwencji UML dla przypadku użycia AnnounceTournament, część odpowie-
dzialna za organizację nowego turnieju
5.6. Analiza przypadku — system AREIMA 253

Diagram z rysunku 5.20 jest o tyle bardziej interesujący, że prowadzi do zidentyfikowa-


nia nowych skojarzeń i atrybutów. W związku ze sponsoringiem obiekt sterujący musi
wpierw uzyskać listę zainteresowanych sponsorów; listę taką utrzymuje obiekt klasy Arena
— dokładniej: przez cały czas utrzymuje on listę wszystkich zarejestrowanych reklamodaw-
ców (Advertiser) — może ją więc po prostu udostępnić wspomnianemu obiektowi ste-
rującemu (i ewentualnie innym obiektom sterującym w innych przypadkach użycia).
Aby wysłać reklamodawcy powiadomienie, potrzebujemy odpowiedniej informacji kontak-
towej, na przykład adresu e-mail, bądź też możemy utworzyć w systemie ARENA prywatne
skrzynki powiadomień dla poszczególnych reklamodawców. W związku z tym dodajemy
do klasy Adverti ser nowy atrybut — informacje kontaktowe — na którego wartość składać
się będzie początkowo adres e-mail reprezentowanego reklamodawcy, a w przyszłości praw-
dopodobnie także dane dotyczące innych kanałów komunikacji. Przewidując analogiczną
potrzebę w stosunku do innych aktorów, dodajemy atrybut kontaktowy także do klas League
"-•Owner i Player.

Rysunek 5.20. Diagram sekwencji UML dla przypadku użycia AnnounceTournament, część odpowie-
dzialna za sponsoring

Konstruując diagram z rysunku 5.21, uświadomiliśmy sobie, że przypadek użycia nie


precyzuje sposobu powiadamiania sponsorów. W konsekwencji dodaliśmy do niego nowy
krok, obejmujący powiadamiania wszystkich sponsorów, którzy odpowiedzieli na ogłoszenie
kapitana ligi, o jego decyzji wyboru wyłącznego sponsora. Krok ten wymaga kolejnego obiektu
brzegowego — SponsorNoti ce. Pozostała część interakcji nie wnosi nic nowego, poza po-
twierdzeniem słuszności naszych przewidywań, prowadzących do uprzedniego zdefiniowania
klas InterestGroup i InterestGroupNotice.
254 Rozdział 4. •Zbieraniewymagań

Rysunek 5.21. Diagram sekwencji UML dla przypadku użycia AnnounceTournament, część odpowie-
dzialna za powiadamianie zainteresowanych grup

5.6.5. Weryfikacja i konsolidacja modelu analitycznego


Skoro zdefiniowaliśmy już większość obiektów uczestniczących w przypadku użycia Announce
^Tournament, wraz z ich atrybutami i skojarzeniami, udokumentujemy rezultat naszej anali-
zy w postaci diagramu klas, a dokładniej — w postaci kilku diagramów, zidentyfikowaliśmy
bowiem dość znaczącą liczbę obiektów. Diagramy te można wykorzystać jako indeks do opra-
cowanego wcześniej słownika — jakkolwiek trudno oczekiwać, by wspomniane diagramy mogły
być interesujące same z siebie dla klienta i użytkowników, mogą się jednak okazać przydatne
dla generowania dodatkowych pytań przy okazji kolejnych wywiadów z klientem.
Skupimy się najpierw na obiektach encji, te bowiem, jako reprezentujące koncepcje
dziedziny aplikacyjnej, wymagają szczególnie starannej weryfikacji ze strony klienta (patrz
rysunek 5.22). Zwróćmy uwagę na naczelną rolę obiektu klasy Arena: reprezentuje on kon-
kretną instancję systemu ARENA i udostępnia wszelkie informacje o charakterze globalnym
dla tej instancji — listy grup zainteresowań (InterestGroup), reklamodawców (Adverti ser),
kapitanów lig (LeagueOwner), gier (Game) i stylów rozgrywek (TournamentStyl e). Zauważmy
także, iż obiekt Arena wyznacza pewną granicę: żaden z obiektów systemu nie jest współdzie-
lony między dwie lub więcej instancji — przykładowo każdy kapitan ligi (LeagueOwner)
należy do dokładnie jednej instancji. Jeżeli dana osoba fizyczna zabawia się wieloma instan-
cjami systemu ARENA, w każdej z tych instancji ma osobne konto i reprezentowana jest przez
osobny obiekt klasy LeagueOwner. Jest to wynik decyzji, jakie podjęliśmy na etapie analizy
systemu, bazując na naszej interpretacji deklaracji problemu, naszym doświadczeniu i naszej oce-
nie zasobów dostępnych dla zbudowania systemu. Oczywiście, wszystkie decyzje tej rangi
bezwzględnie muszą być zweryfikowane i zatwierdzone przez klienta.
5.6. Analiza przypadku — system AREIMA 255

Rysunek 5.22. Obiekty encji zidentyfikowane na podstawie analizy przypadku użycia Announce
^Tournament

Kolejny nasz diagram klas (ten z rysunku 5.23) uwidacznia relacje dziedziczenia między
poszczególnymi klasami. Jakkolwiek język UML umożliwia reprezentowanie skojarzeń i relacji
dziedziczenia na tym samym diagramie klas, zalecaną praktyką jest rysowanie dwóch odręb-
nych diagramów. Powody tego są dwa: po pierwsze, skojarzenia i dziedziczenie reprezento-
wane są za pomocą podobnych symboli, które łatwo pomylić ze sobą, po drugie — skojarzenia
między klasami i hierarchia klas to zagadnienia, którymi analitycy zajmują się w różnym
czasie; jak jednak zobaczymy w następnych czterech rozdziałach, nie jest to prawda na etapie
projektowania systemu i projektowania obiektów, kiedy to często konieczne jest uwzględnianie
obu relacji dla lepszego zrozumienia powiązań między klasami.
Na rysunku 5.30 widoczne są trzy hierarchie dziedziczenia. Pierwsza z nich rozpoczyna
się od abstrakcyjnej klasy User, zdefiniowanej w wyniku generalizacji i reprezentującej (w po-
staci odpowiednich atrybutów) własności wspólne dla wszystkich użytkowników, takie jak
informacje kontaktowe i procedury rejestracyjne. Zauważmy przy tym, że terminu „użytkow-
nik" (user) używaliśmy już wielokrotnie w deklaracji problemu i w przypadkach użycia, tak
więc dokonujemy teraz formalizacji terminu stosowanego dotąd w znaczeniu intuicyjnym.
Dwie pozostałe hierarchie są wynikiem specjalizacji Idas Game i TournamentStyle, reprezen-
tujących koncepcje (odpowiednio) gry i stylu rozgrywek. Klasy Ti cTacToe i Chess, stanowiące
wynik specjalizowania klasy Game, odpowiadają (kolejno) grze w kółko i krzyżyk oraz szachom.
256 Rozdział 4. •Zbieraniewymagań

Rysunek 5.23. Hierarchia dziedziczenia między klasami obiektów encji przypadku użycia Announce
^Tournament

Klasy KnockOutStyle i RoundRobinStyle, jako specjalizacje klasy TournamentStyle, odpowia-


dają stylowi „przegranej przez nokaut", zgodnie z którym gracz po pierwszej przegranej
odpada z turnieju, i stylowi „karuzelowemu", zgodnie z którym każdy gracz pojedynkuje się
dokładnie jeden raz z każdym z pozostałych graczy.
Wreszcie na kolejnym diagramie klas (patrz rysunek 5.24) uwidoczniliśmy skojarzenia
między obiektami brzegowymi, obiektem sterującym i wybranymi obiektami encji. Diagram
ten stworzyliśmy pośrednio, najpierw generując diagram komunikacyjny z diagramu sekwencji
i umieszczając obiekt sterujący z lewej strony, obiekty brzegowe pośrodku, z prawej zaś obiekty
encji; następnie tam, gdzie to potrzebne, zastąpiliśmy interakcje przez skojarzenia, które do-
datkowo opatrzyliśmy „nawigacjami" w celu ukazania kierunku zależności: obiekty sterujące
i brzegowe zawierają informacje o wszystkich innych obiektach, podczas gdy obiekty encji
niezależne są od obiektów dwóch pozostałych kategorii.
Podczas gdy diagram klas z rysunku 5.22 koncentruje się głównie na relacjach między
koncepcjami dziedziny aplikacyjnej, diagram widoczny na rysunku 5.24 uwidacznia (w przy-
bliżeniu) koncepcje związane z przepływem sterowania w ramach przypadku użycia. Obiekt
sterujący służy tu jako spoiwo między obiektami brzegowymi i obiektami encji, reprezentuje
bowiem koordynację i chronologię między formularzami a powiadomieniami. Na diagramach
z rysunków 5.19, 5.20 i 5.21 obiekt sterujący odpowiedzialny jest za tworzenie niektórych
obiektów brzegowych. Diagram widoczny na rysunku 5.24 jest swego rodzaju sumarycznym
zestawienie obiektów biorących udział w przypadku użycia, z uwzględnieniem skojarzeń wy-
korzystywanych do realizacji tego przypadku. Jednakże to diagram sekwencji dostarcza pełną
informację na temat sekwencjonowania operacji i przepływu sterowania.

5.6.6. Wnioski
W tej sekcji stworzyliśmy część analitycznego modelu obiektowego, odpowiadającą przy-
padkowi użycia AnnounceTournament w systemie ARENA. Rozpoczęliśmy od zidentyfikowania
obiektów encji, wykorzystując heurystyki Abbotta, po czym zidentyfikowaliśmy obiekty brze-
gowe i obiekt sterujący, by następnie za pomocą diagramów sekwencji odnaleźć dodatkowe
5.6. Analiza przypadku — system AREIMA 257

Rysunek 5.24. Skojarzenia między obiektami brzegowymi, obiektem sterującym i wybranymi obiektami
encji przypadku użycia AnnounceTournament

skojarzenia, atrybuty i obiekty. Na zakończenie skonsolidowaliśmy model obiektowy i uka-


zaliśmy go w formie serii diagramów klas. Na podstawie tych czynności czytelnicy mogli na-
uczyć się, że:

• identyfikowanie obiektów oraz ich atrybutów i skojarzeń wymaga wielu iteracji,


często z udziałem klienta,
• identyfikowanie obiektów wymaga skorzystania z wielu źródeł, między innymi
deklaracji problemu, modelu przypadków użycia, słownika i opisu przepływu
zdarzeń w przypadkach użycia,
• każdy niebanalny przypadek użycia wymaga wielu diagramów sekwencyjnych i pew-
nej liczby diagramów klas; niewykonalne jest przedstawienie wszystkich rozpozna-
nych obiektów na pojedynczym diagramie, zamiast tego wykorzystuje się więc kilka
rodzajów diagramów, służących różnym celom — przykładowo ukazaniu skojarzeń
między obiektami encji czy między wszystkimi obiektami uczestniczącymi w poje-
dynczym przypadku użycia,
• produkty docelowe, istotne dla całej realizacji projektu, powinny być na bieżąco ak-
tualizowane w miarę wprowadzania zmian do modelu analitycznego; inne produkty,
takie jak diagramy sekwencji, mogą być uaktualniane rzadziej lub z opóźnieniem
— utrzymanie bezwzględnej spójności modelu jest bowiem zadaniem nierealnym,
• istnieje wiele sposobów modelowania tej samej dziedziny aplikacyjnej lub tego sa-
mego systemu, zależnie od osobistego stylu i doświadczenia analityka; wymaga to
przyjęcia pewnego standardu dotyczącego wspomnianego stylu i konwencji w pro-
jekcie, tak by poszczególni analitycy mogli się ze sobą efektywnie komunikować.
258 Rozdział 4. •Zbieraniewymagań

5.7. Literatura uzupełniająca


Podział obiektów modelu analitycznego na trzy kategorie encji, brzegowe i sterujące — stał się
popularny dzięki książce I. Jacobsona, M. Christersona, P. Jonssona i G. Overgaarda [Jacobson
i in., 1992], Ów podział ma swój pierwowzór w paradygmacie „model-widok-kontroler"
(MVC — Model-View-Controller) zastosowanym praktycznie po raz pierwszy w środowisku
Smalltalk-80; jego odmiana znalazła swe miejsce w środowisku Swing języka Java [JFC, 2009].
Karty CRC zaproponowane zostały przez K. Becka i W. Cunninghama jako środek
pomocny w przyswajaniu myślenia zorientowanego obiektowo.. Autorzy opisali je w ma-
teriałach konferencji OOPSLA [Beck i Cunningham, 1989]. Karty CRC wykorzystywane są
także w metodologii projektowania sterowanego odpowiedzialnością opisanej przez R. Wirfs-
Brock, B. Wilkersona i L. Wiener [Wirfs-Brock i in., 1990],
Analiza i projektowanie zorientowane obiektowo to wynik ewolucji wielu zbiorów roz-
maitych heurystyk i terminologii. Modelowanie, podobnie jak programowanie, jest rzemio-
słem wymagającym doświadczenia z jednej strony, a świadomości nieuchronnego popełniania
błędów z drugiej. Wynika stąd krytyczna rola sprawnej komunikacji między programistami
a klientem i użytkownikami. Książka J. Rumbaugha, M. Błahy, W. Premerlaniego, F. Eddy'ego,
i W. Lorensena [Rumbaugh i in., 1991] jest doskonałym przewodnikiem dla nowicjuszy w mo-
delowaniu opartym na klasach. Obszerne studium analizy i projektowania zorientowanych
obiektowo, w tym modelowania w oparciu o przypadki użycia i wielokrotne wykorzystywanie
wzorców projektowych, znajdą czytelnicy w nowszej książce C. Larmana [Larman, 2005].
Z kolei książka B. P. Dougłassa [Douglass, 1999] dostarcza szczegółowe informacje na temat
modelowania dynamicznego w oparciu o diagramy stanów, wraz z interesującymi.heurysty-
kami w tym zakresie.

5.8. Ćwiczenia
5.1. Rozpatrzmy system plików z graficznym systemem użytkownika, na przykład
Windows Explorer, Finder Macintosha czy linuksowy KDE. W przypadku użycia
opisującym kopiowanie pliku z dyskietki na dysk twardy zidentyfikowano następujące
obiekty: Fi 1 e (plik), Icon (ikona), TrashCan (kosz), Fol der, Di sk, Poi nter (wskaźnik).
Które z nich są obiektami encji, które brzegowymi, a które sterującymi?
5.2. W systemie wspomnianym w ćwiczeniu 5.1 rozpatrzmy scenariusz obejmujący wy-
bór pliku (File) na dyskietce, przeciągnięcie myszą wybranej pozycji do folderu
(Folder) i zwolnienie lewego przycisku myszy. Zidentyfikuj i zdefiniuj choć jeden
obiekt sterujący uczestniczący w tym scenariuszu.
5.3. Zorganizuj obiekty, o których mowa w ćwiczeniach 5.1 i 5.2, w poziomą strukturę
typową dla diagramu sekwencji: począwszy od lewej, najpierw obiekty brzegowe,
potem sterujące i na końcu obiekty encji. Narysuj sekwencję interakcji prowadzącą
do upuszczenia wybranego pliku w folderze docelowym. Zignoruj możliwe sytuacje
wyjątkowe.
5.4. Przeanalizuj diagram sekwencji utworzony w ćwiczeniu 5.3 i zidentyfikuj skojarzenia
między obiektami.
5.8. Ćwiczenia 259

5.5. Zidentyfikuj atrybuty każdego' obiektu mające związek ze scenariuszem opisanym


w ćwiczeniu 5.2. Uwzględnij sytuacje wyjątkowe polegające na:
a) braku wolnego miejsca na dysku docelowym,
b) istnieniu w folderze docelowym pliku o takiej samej nazwie jak plik właśnie
kopiowany.
5.6. Na rysunku 5.25 widoczny jest model obiektowy (zaczerpnięty z książki M. Jacksona
[Jackson, 1995]). Opierając się na powszechnie znanych właściwościach kalendarza
gregoriańskiego, wymień problemy, jakie stwarzać może ów model i zaproponuj jego
modyfikację pod kątem wyeliminowania tych problemów.

Rysunek 5.25. Naiwny model kalendarza gregoriańskiego

5.7. Czy operując jedynie krotnościami skojarzeń, możesz tak zmodyfikować model wi-
doczny na rysunku 5.25, by programista nieobeznany z kalendarzem gregoriańskim
potrafił wydedukować liczbę dni w każdym miesiącu? Zidentyfikuj ewentualne do-
datkowe klasy potrzebne do wykonania tego zadania.
5.8. Rozpatrz system sygnalizacji świetinej na skrzyżowaniu dwóch dróg dwukierunko-
wych. Zakładając najprostszy algorytm sterowania ruchem (dopuszczenie do ruchu
na jednej z dróg, przy jednoczesnym zablokowaniu ruchu w kierunku prostopadłym,
z cyldiczną zmianą), zidentyfikuj stany tego systemu i narysuj diagram stanów ilu-
strujący jego funkcjonowanie. Dla każdego sygnalizatora zakładamy trzy stany, od-
powiadające światłom: zielonemu, żółtemu i czerwonemu.
5.9. Narysuj diagram Was odpowiadający diagramowi sekwencji z rysunku 2.29. Wska-
zówka: Rozpocznij od zidentyfikowania obiektów na wspomnianym diagramie.
5.10. Rozpatrz dodanie wymagania pozafunkcyjnego stanowiącego, że działania zmie-
rzające do wyłącznego sponsorowania turnieju wymagać mają jak najmniejszego
wysiłku ze strony reklamodawcy. Zmodyfikuj w związku z tym przypadki użycia
AnnounceTournament (patrz tabela 5.8) i ManageAdvertisements (rozwiązanie ćwi-
czenia 4.12), tak by reklamodawca mógł zaznaczyć w swym profilu opcję powo-
dującą, iż na otrzymane od kapitana ligi zapytanie o chęć wyłącznego sponsorowa-
nia automatycznie udzielona zostanie odpowiedź twierdząca.
260 Rozdział 4. •Zbieraniewymagań

5.11. Z i d e n t y f i k u j i z d e f i n i u j d o d a t k o w e o b i e k t y encji, b r z e g o w e i sterujące, jakie t r z e b a


b y w p r o w a d z i ć d o p r z y p a d k u użycia AnnounceTournament w celu z r e a l i z o w a n i a
z m i a n y o p i s a n e j w ć w i c z e n i u 5.10.

5.12. U a k t u a l n i j d i a g r a m y klas z r y s u n k ó w 5.22 i 5.24 tak, b y u w z g l ę d n i a ł y n o w e obiekty,


o k t ó r y c h m o w a w ć w i c z e n i u 5.11.

5.13. Bazując n a d i a g r a m a c h sekwencji w i d o c z n y c h n a r y s u n k a c h 5.19, 5.20 i 5.21, narysuj


d i a g r a m s t a n ó w o p i s u j ą c y z a c h o w a n i e o b i e k t u AnnounceTournamentControl. Po-
t r a k t u j k a ż d e wysłanie p o w i a d o m i e n i a i k a ż d e o t r z y m a n i e p o w i a d o m i e n i a j a k o z d a -
rzenie w y z w a l a j ą c e z m i a n ę s t a n u .

Bibliografia
[Abbott, 1983] R. Abbott „Program design by informal English descriptions",
Communications of the ACM, t. 26, nr 11, 1983.

[Beck i Cunningham, 1989] K. Beck, W. C u n n i n g h a m „A laboratory for teaching object-oriented


thinking" OOPSLA'89 Conference Proceedings, New Orleans, LA,
1 - 6 października 1989.
[De Marco, 1978] T. De Marco Structured Analysis and System Specification, Yourdon,
New York, 1978.
[Douglass, 1999] B. P. Douglass Doing Hard Time: Using Object Oriented Programming
and Software Patterns in Real Time Applications, Addison-Wesley,
Reading, MA, 1999.

[Jackson, 1995] M. Jackson Software Requirements i Specifications: A Lexicon of Practice,


Principles and Prejudices, Addison-Wesley, Reading, MA, 1995.

[Jacobson i in., 1992] I. Jacobson, M. Christerson, P. Jonsson, G. Overgaard Object-Oriented


Software Engineering — A Use Case Driven Approach, Addison-Wesley,
Reading, MA, 1992.

[Jacobson i in., 1999] I. Jacobson, G. Booch, J. Rumbaugh The Unified Software Development
Process, Addison-Wesley, Reading, MA, 1999.

[JFC, 2009] Java Foundation Classes, JDK Documentation, Javasoft, 2009.

[Larman, 2005] C. Larman Applying UML and Patterns: An Introduction to


Object-Oriented Analysis and Design, wyd. trzecie, Prentice Hall,
Upper Saddle River, NJ, 2005.

[Rumbaugh i in., 1991] J. R u m b a u g h , M. Błaha, W. Premerlani, F. Eddy, W . Lorensen


Object-Oriented Modeling and Design, Prentice Hall, Englewood Cliffs,
NJ, 1991.

[Wirfs-Brock i in., 1990] R. Wirfs-Brock, B. Wilkerson, L. Wiener Designing Object-Oriented


Software, Prentice Hall, Englewood Cliffs, NJ, 1990.
6.1. Wstęp: projekt mieszkania 264

6.2. O projektowaniu systemu ogólnie 266

6.3. Koncepcje projektowania systemu 267


6.3.1. Podsystemy i klasy 268
6.3.2. Usługi i interfejsy podsystemów 270
6.3.3. Sprzężenie i spoistość 271
6.3.4. Warstwy i partycje 275
6.3.5. Style architektoniczne 279
6.4. Aktywności projektowania systemu:
od obiektów do podsystemów 288
6.4.1. Punkt wyjścia: model analityczny
systemu planowania podróży 288
6.4.2. Identyfikowanie celów projektowych 290
6.4.3. Identyfikowanie podsystemów 294

6.5. Literatura uzupełniająca 296

6.6. Ćwiczenia 297

Bibliografia 298
6
Projektowanie systemu
— dekompozycja
na podsystemy
Oprogramowanie można tworzyć na dwa sposoby: tak prosto,
że w oczywisty sposób będzie wolne od błędów, albo tak skomplikowanie,
że nie będzie w nim oczywistych błędów.
C. A. R. Hoare The Emperor's Old Clothes

^IProjektowanie systemu to transformowanie modelu analitycznego w model projektu


systemu. W trakcie projektowania systemu programiści definiują cele projektowe i doko-
nują dekompozycji systemu na prostsze podsystemy, z których każdy może być realizowa-
ny przez pojedynczy zespół. Programiści wybierają także strategie budowania systemu —
strategie sprzętowe i programowe, strategie trwałego przechowywania danych, globalne
sterowanie przepływem informacji, politykę kontroli dostępu i obsługę sytuacji granicz-
nych. Wynikiem tych działań jest model odzwierciedlający wymienione strategie i wspo-
mniany podział systemu na podsystemy.
Projektowanie systemu nie jest działalnością algorytmiczną: programiści zmuszeni są
kompromisów w związku z licznymi celami projektowymi, często sprzecznymi ze sobą. Nie
potrafią ponadto przewidywać a priori wszystkich przyszłych problemów, nie mają bowiem
dostatecznie jasnego obrazu dziedziny realizacyjnej.
Samo projektowanie systemu też da się przedstawić w postaci dekompozycji na poszcze-
gólne aktywności, z których każda związana jest z określoną częścią ogólnie pojmowanej
dekompozycji na podsystemy. Oto one.

• Rozpoznawanie celów projektowych. Programiści identyfikują zakładane cechy


systemu i określają priorytety ich optymalizacji.
• Projektowanie wstępnej dekompozycji. Programiści dekomponują system na prostsze
części, bazując na modelu przypadków użycia i modelu analitycznym. Jako punktu
wyjścia wykorzystują w tym celu standardowe style architektoniczne.
• Doskonalenie dekompozycji stosownie do celów projektowych. Początkowy wynik de-
kompozycji systemu rzadko kiedy czyni zadość celom projektowym, wymaga więc
optymalizacji pod tym właśnie kątem.

W tym rozdziale skupimy się na dwóch pierwszych z wymienionych aktywności, w na-


stępnym natomiast udoskonalimy dekompozycję i obszernie omówimy jej szczegóły na przy-
kładzie zastosowania do systemu ARENA.
264 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

6.1. Wstęp: projekt mieszkania


Na konstruowanie systemu składają się: jego projektowanie, projektowanie jego obiektów
i implementowanie. W ramach każdej z tych aktywności zadanie programistów postrzegać
można jako wypełnianie luki między specyfikacją wymagań, wieńczącą zbieranie wymagań
od klienta, a gotowym systemem, którego dostarczenia oczekują klient i użytkownicy.
Projektowanie systemu jest pierwszym krokiem na tej drodze i skupia się na jego podziale
na mniejsze części, łatwiejsze do ogarnięcia. Na etapie zbierania wymagań i ich analizy wy-
siłek programistów koncentruje się na zrozumieniu przeznaczenia systemu i szczegółów jego
funkcjonalności; w czasie projektowania systemu kluczowymi problemami są procesy, struk-
tury danych oraz komponenty sprzętowe i programowe niezbędne do jego implementacji.
Pierwszym znaczącym wyzwaniem jest wówczas pogodzenie sprzecznych kryteriów i ograni-
czeń wynikających z dekompozycji systemu.
Jako przykład procesu generującego analogiczne wyzwania rozpatrzmy zadanie za-
projektowania układu nowego mieszkania. Gdy już porozumiemy się z klientem co do lokali-
zacji domu, liczby pomieszczeń, powierzchni salonu i tak dalej, zadaniem architekta jest
podział powierzchni przyszłego mieszkania na poszczególne pomieszczenia, czyli zaprojekto-
wanie rozmieszczenia ścian, drzwi i okien. Zadanie to musi zostać wykonane z uwzględnie-
niem wielu wymagań funkcyjnych: przykładowo kuchnia powinna być zlokalizowana jak
najbliżej jadalni i jednocześnie jak najbliżej garażu, łazienka powinna znajdować się jak najbliżej
sypialni i tak dalej. Architekt musi także liczyć się z kilkoma ograniczeniami: wyposażenie
kuchni składać się będzie ze standardowych modułów (mebli, kuchenek, zmywarki), zaś w sy-
pialni znajdą się łóżka czy leżanki, których wymiary też są zestandaryzowane — wszystko to
ma wpływ na (rozsądne) rozmieszczenie okien i drzwi. Architekta nie interesują już jednak
takie aspekty urządzenia mieszkania jak umeblowanie poszczególnych pomieszczeń czy kolor
parkietu, te bowiem zostawiane są do wyboru klientowi.
Na rysunku 6.1 widoczna jest krótka historia ewoluowania opisanej koncepcji archi-
tektonicznej; jej autorowi postawiono wymóg stosowania się do następujących ograniczeń.

1. W mieszkaniu znajdować się powinny dwie sypialnie, studio, kuchnia i salon.


2. Droga, jaką prawdopodobnie pokonywali będą mieszkańcy w trakcie codziennych
czynności domowych, powinna być sumarycznie jak najmniejsza.
3. Należy maksymalnie wykorzystywać światło dzienne.

W związku z treścią powyższych wymagań warto zauważyć, że najbardziej intensywna


wędrówka mieszkańców dokonuje się w trzech obszarach: między drzwiami wejściowymi
a kuchnią (gdy żywność i produkty towarzyszące wyładowywane są z samochodu), między
kuchnią a jadalnią (gdy posiłki podawane są na stół) oraz między sypialniami a łazienką (ła-
zienkami). Zakładamy przy tym, że mieszkańcy większą część czasu spędzają w salonie lub
jadalni oraz głównej sypialni.
W górnej części rysunku 6.1 widoczna jest wersja nr 1 projektu, w której rychło spo-
strzec można poważny mankament: jadalnia oddalona jest za bardzo od kuchni. By naprawić
to niedomaganie, zamieniamy kuchnię z drugą sypialnią (co symbolizują szare strzałki), dzięki
czemu dodatkowo salon przesuwa się ku południowej ścianie domu. Teraz jednak — w wersji
6.1. Wstęp: projekt mieszkania 265

Rysunek 6.1. Przykład projektu układu pomieszczeń w mieszkaniu. Kolejne wersje coraz lepiej reali-
zują wymaganie zmniejszenia łącznej drogi pokonywanej przez mieszkańców oraz jak najpełniejsze
wykorzystywanie światła dziennego

nr 2 — kuchnia i schody znajdują się zbyt daleko od drzwi wejściowych. W wersji nr 3 prze-
sunięcie drzwi wejściowych na ścianę północną usuwa ten problem, dodatkowo pozwalając
na zbliżenie drugiej sypialni do sypialni głównej i przesunięcie łazienki bliżej obu sypialni.
Efektem ubocznym jest powiększenie powierzchni salonu. Wszystko to odbywa się w zgodzie
z wymienionymi na wstępie ograniczeniami.
266 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Mając już ustalony rozkład poszczególnych pomieszczeń, możemy zająć się lokalizacją
okien i drzwi tak, by jak najlepiej spełnione zostały wymienione wymagania. To zadanie koń-
czy projekt, bez określenia szczegółowego umeblowania poszczególnych pomieszczeń. Można
teraz przystąpić do projektowania sieci elektrycznej, wodociągowej, kanalizacyjnej i grzew-
czej budynku.
Nieprzypadkowo sięgnęliśmy po analogię architektoniczną, projektowanie domów
i mieszkań ma bowiem wiele wspólnego z inżynierią oprogramowania, co pokrótce uzasad-
niamy w tabeli 6.1. W obu przypadkach zadanie projektowania podzielone jest na prostsze
komponenty i interfejsy, w obu mamy do czynienia z wymaganiami funkcyjnymi i pozafunk-
cyjnymi, oba też niosą ze sobą jednakowo poważne konsekwencje ewentualnych pomyłek i nie-
dopatrzeń. Oba wreszcie przypadki abstrahują od szczegółowego projektu poszczególnych
komponentów.

Tabela 6.1. Dwie siostry — architektura i inżynieria oprogramowania

Koncepcja inżynierii
Koncepcja architektoniczna
oprogramowania
Komponenty Pomieszczenia Podsystemy
Interfejsy Drzwi Usługi
Wymagania pozafunkcyjne Maksymalizacja powierzchni Minimalizacja czasu reakcji
mieszkalnej

Wymagania funkcyjne D o m mieszkalny Przypadki użycia


Realia przeróbek Przestawianie ścian Zmiana interfejsów podsystemów

W sekcji 6.2 przedstawiamy widok „z lotu ptaka" na projektowanie systemu i związek


tego projektowania z analizą wymagań. W sekcji 6.3 przedstawiamy koncepcję podsystemów
i dekompozycji, zaś sekcję 6.4 poświęcamy aktywnościom związanym z projektowaniem systemu
i pokazujemy na przykładzie, jak poszczególne podsystemy powinny ze sobą współpracować.

6.2. O projektowaniu systemu ogólnie


Rezultatem analizy wymagań jest model, który tworzą następujące produkty:

• zbiór wymagań pozafunkcyjnych i ograniczeń, takich jak maksymalny czas reakcji,


minimalna przepustowość, niezawodność czy platforma systemu operacyjnego,
• model przypadków użycia, opisujący zachowanie systemu z perspektywy współdzia-
łających z nim aktorów,
• model obiektowy, opisujący encje będące przedmiotem przetwarzania systemu,
• diagram sekwencji dla każdego przypadku użycia, ukazujący sekwencję interakcji
między obiektami uczestniczącymi w danym przypadku użycia.

Model analityczny jest opisem systemu widzianego oczami aktorów i stanowi podstawę
komunikacji między klientem a programistami. Model analityczny nie zawiera jednak informacji
na temat wewnętrznej struktury systemu, jego konfiguracji sprzętowej i, ogólnie rzecz ujmując,
6.3. Koncepcje projektowania systemu 267

sposobów jego realizacji. Pierwszym krokiem w tym kierunku jest bowiem projektowanie sys-
temu, którego rezultat stanowią następujące produkty:

• cele projektowe, opisujące cechy systemu stanowiące dla programistów przedmiot


optymalizacji,
• architektura programowa, ujmująca dekompozycję na podsystemy w kategoriach
odpowiedzialności każdego z nich, zależności między nimi, ich odwzorowania
w komponenty sprzętowe i ogólne założenia dotyczące między innymi kontroli do-
stępu, przepływu sterowania i strategii trwałego przechowywania danych,
• graniczne przypadki użycia, opisujące sytuacje brzegowe, ekstremalne lub wyjątkowe
związane z systemem: konfigurowanie, instalowanie, uruchamianie, zamykanie i re-
agowanie na występujące błędy.

Cele projektowe formułowane są na podstawie wymagań pozafunkcyjnych. Stanowią


one dla programistów swoisty przewodnik, w sytuacji gdy trzeba dokonywać wyborów, czy
decydować o priorytetach w kręgu sprzecznych przesłanek i wymagań. Lwią część projek-
towania systemu stanowi jego dekompozycja: każdy podsystem powstały w wyniku tej de-
kompozycji przeznaczony jest do opracowania niezależnie przez osobny zespół — w ten
oto sposób programiści radzą sobie z ogólną złożonością, sprowadzając pojedynczy złożony
problem do kilku prostszych podproblemów, z których każdy możliwy jest do ogarnięcia
przez jedną osobę lub przynajmniej pojedynczy zespół. Ponieważ poszczególne podzespoły
realizują swoje podsystemy niezależnie, konieczne jest uprzednie rozwiązanie problemów
i wątpliwości dotyczących systemu jako całości.
W tym rozdziale opiszemy koncepcje dekompozycji systemu na podsystemy i zaprezen-
tujemy przykłady konkretnych wzorców dekompozycyjnych, w inżynierii oprogramowania
zwanych popularnie „stylami architektonicznymi". W następnym rozdziale omówimy do-
skonalenie dekompozycji, mające na celu osiągnięcie specyficznych celów projektowych.
Na rysunku 6.2 przedstawiliśmy poglądowo usytuowanie projektowania systemu w kon-
tekście innych aktywności inżynierii oprogramowania.

6.3. Koncepcje projektowania systemu


W tej sekcji opiszemy szczegółowo dekompozycję systemu i jej właściwości. Rozpoczniemy
od zdefiniowania koncepcji podsystemu i jego związku z klasami (patrz sekcja 6.3.1), po
czym zajmiemy się interfejsem podsystemu (patrz sekcja 6.3.2): podsystemy dostarczają usług
dla innych podsystemów — usługa jest zbiorem powiązanych operacji podporządkowanych
wspólnemu celowi. W trakcie projektowania systemu poszczególne podsystemy rozważane
są najpierw głównie w kontekście oferowanych przez siebie usług; dla programisty zewnętrzny
obraz podsystemu wyraża się w postaci jego interfejsu, czyli zestawu udostępnianych operacji.
W sekcji 6.3.3 zajmiemy się dwiema ważnymi właściwościami podsystemów: sprzężeniem
i spoistością. Sprzężenie między dwoma podsystemami jest miarą ich wzajemnej zależności,
podczas gdy spoistość charakteryzuje zależności pomiędzy klasami pojedynczego podsystemu.
Ideałem dekompozycji jest uzyskanie minimalnego sprzężenia między poszczególnymi pod-
systemami i maksymalnej spoistości każdego z nich. Podsystemy mogą być powiązane na dwa
268 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Rysunek 6.2. Aktywności związane z projektowaniem systemu

różne sposoby, którymi zajmiemy się w sekcji 6.3.4: architektura warstwowaoznacza hie-
rarchiczną organizację podsystemów, w ramach której każda warstwa hierarchii dostarcza
usługi na potrzeby warstw wyższych, jednocześnie korzystając z usług oferowanych przez
warstwy niższe, natomiast partycjonowanie oznacza organizację podsystemów jako wzajem-
nych partnerów, nawzajem świadczących sobie usługi. W sekcji 6.3.5 przedstawimy natomiast
kilka typowych, najczęściej spotykanych w praktyce przykładów stylów architektonicznych
dekompozycji.

6.3.1. Podsystemy i klasy


W rozdziale 2. „Modelowanie w języku UML" wprowadziliśmy rozróżnienie między dziedziną
aplikacyjną a dziedziną realizacyjną. W celu zredukowania złożoności dziedziny aplika-
cyjnej wprowadziliśmy jej podział na części zwane „klasami", które następnie zorganizo-
waliśmy w pakiety. Podobnie w celu redukcji złożoności dziedziny realizacyjnej podzielili-
śmy system na prostsze części, zwane podsystemami; każda z tych części składa się z klas
dziedziny realizacyjnej. Podsystem jest wymienną częścią systemu, posiadającą dobrze
zdefiniowane interfejsy i hermetyzującą stan oraz zachowanie składających się na nią klas.
Pracochłonność stworzenia podsystemu nie przekracza zwykle możliwości pojedynczego
zespołu lub nawet pojedynczego programisty, choć w przypadku systemów bardzo złożo-
nych można stosować dekompozycję w sposób rekursywny (co pokazano schematycznie
na rysunku 6.3), dzieląc poszczególne podsystemy na części jeszcze prostsze. Ponieważ po-
szczególne podsystemy z założenia uzależnione są od siebie w jak najmniejszym stopniu,
konkretne zespoły mogą pracować nad nimi względnie niezależnie, z niewielkim narzutem
na komunikację z innymi zespołami.
6.3. Koncepcje projektowania systemu 269

Rysunek 6.3. Dekompozycja systemu

I tak na przykład wielokrotnie już cytowany system FRI END można w wyniku dekompo-
zycji podzielić na kilka prostszych podsystemów:

• Di s p a t c h e r l n t e r f a c e — realizujący interfejs użytkownika dla dyspozytora


(Di s p a t c h e r ) ,
• FieldOfficerlnterface — realizujący interfejs użytkownika dla funkcjonariusza
(FieldOfficer),

• IncidentManagement — odpowiedzialny za tworzenie, modyfikowanie i trwałe


przechowywanie obiektów Incident reprezentujących poszczególne wypadki,
• ResourceManagement — zarządzający dostępnymi zasobami (między innymi wozami
strażackimi i karetkami pogotowia),
• MapManagement — utrzymujący informację geograficzną w postaci map i słownych
opisów lokalizacji,
• Noti f i cati on — implementujący komunikację między laptopem funkcjonariusza
a stacją roboczą dyspozytora.

Dekompozycję tę przedstawiliśmy schematycznie na diagramie komponentów UML,


widocznym na rysunku 6.4. Poszczególne komponenty przedstawione są w postaci prostokątów,
opatrzonych specjalną ikoną S , oznaczającą w języku UML komponent większej całości.
Zależności między komponetami reprezentowane są przez skierowane linie przerywane. Język
UML rozróżnia dwa rodzaje komponentów: komponent logiczny odpowiada podsystemowi
niemającemu bezpośredniego fizycznego odpowiednika w świecie rzeczywistym — komponen-
tem takim może być warstwa logiczna realizująca pewien zakres reguł biznesowych; nato-
miast komponent fizyczny to podsystem będący bezpośrednim odzwierciedleniem realnego
bytu, na przykład serwera bazy danych.
Niektóre języki programowania (między innymi Java i Modula 2) dostarczają programi-
ście konstrukcje do modelowania podsystemów (w Javie są to pakiety, w Moduli — moduły).
Inne języki1, takie jak C i C++, nie dostarczają bezpośredniego wsparcia dla podsystemów,
przez co programiści muszą zorganizować owo wsparcie we własnym zakresie, co zwykle czynią

1
We współczesnych implementacjach języka Pascal (Delphi, FreePascal, Lazarus i tym podobnych) ce-
lowi temu służyć mogą moduły (units) i pakiety (packages). Większość współczesnych środowisk pro-
gramistycznych umożliwia także tworzenie bibliotek DLL, będących „cegiełkami", z jakich buduje
się złożony system — czego koronny przykład zaobserwować możemy w systemach linii Windows
— przyp. tłum.
270 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Rysunek 6.4. Diagram komponentowy UML przedstawiający dekompozycję systemu FRIEND; kom-
ponentami są podsystemy stanowiące rezultat tej dekompozycji. Skierowane linie przerywane ozna-
czają zależności między poszczególnymi podsystemami

przez odpowiedni podział plików źródłowych systemu na katalogi i podkatalogi. Niezależnie


jednak od zakresu wsparcia, jakie w zakresie dekompozycji oferują programistom różne ję-
zyki programowania, muszą oni dekompozycję udokumentować szczególnie starannie, bo-
wiem oznaczać ona będzie również dekompozycję realizacji projektu na poszczególne zespoły
programistyczne.

6.3.2. Usługi i interfejsy podsystemów


Z perspektywy użyteczności podsystem scharakteryzować można przez usługi, jakie oferuje
on innym podsystemom. Usługa jest zbiorem powiązanych operacji podporządkowanych
realizacji wspólnego celu. Przykładowo podsystem oferujący usługę powiadamiania definiuje
operacje wykonujące wysyłanie powiadomień, nasłuchiwanie w kanałach komunikacyjnych,
subskrybowanie do tych kanałów i anulowanie subskrypcji i tym podobne. Zbiór operacji, jakie
podsystem udostępnia innym podsystemom, nazywamy jego interfejsem. Każda operacja
wchodząca w skład interfejsu identyfikowana jest przez nazwę oraz scharakteryzowana pod
względem zestawu (liczby i typów) parametrów i (ewentualnie) typu zwracanego wyniku.
Projektowanie systemu skupia swe aktywności na definiowaniu usług świadczonych
przez każdy podsystem, czyli na wymienieniu (enumeracji) poszczególnych operacji, ich pa-
rametrów oraz wysokopoziomowego zachowania. Doskonalenie interfejsów podsystemów,
jakie dokonuje się na etapie projektowania obiektów, daje w wyniku interfejs programisty,
popularnie określany skrótem API (Application Programmer Interface).
Powiązanie danego podsystemu z innymi podsystemami poprzez jego interfejs odzwier-
ciedlone jest w języku UML za pomocą specjalnej notacji złączy, zwanej także potocznie no-
tacją „kółko-gniazdo" (ball-and-socket). Ikona € > będąca sednem tej notacji składa się z dwóch
części: zewnętrzny łuk, zwany popularnie gniazdem (socket), reprezentuje korzystanie z operacji,
6.3. Koncepcje projektowania systemu 271

pozostała część ikony, popularnie nazywana piłką (bali) lub lizakiem (lollipop), odzwierciedla
udostępnianie operacji. Na rysunku 6.5 widoczne jest zastosowanie tej notacji do przedsta-
wienia powiązań podsystemów F i e l d O f f i c e r l n t e r f a c e i Di s p a t c h t e r l n t e r f a c e z pod-
systemem ResourceManagement. Podsystem Fi eldOff i c e r l n t e r f a c e wykorzystuje usługę
ResourceUpdateServi ce do aktualizacji informacji o statusie i lokalizacji funkcjonariusza,
zaś podsystem D i s p a t c h e r l n t e r f a c e korzysta z usługi R e s o u r c e A l l o c a t i o n S e r v i c e
w celu zidentyfikowania dostępnych aktualnie zasobów i przydzielenia ich do obsługi wypadku.
Obie wymienione usługi udostępniane są przez podsystem ResourceManagement.

Rysunek 6.5. Udostępnianie usług przez podsystem ResourceManagement (przykład notacji „kółko-
-gniazdo")

Notacja „kółko-gniazdo" staje się użyteczna dopiero wówczas, gdy proces dekompo-
zycji przybiera zdecydowanie stabilny kształt i gdy po dobrym określeniu poszczególnych
podsystemów przychodzi kolej na definiowanie oferowanych przez nie usług. Gdy brakuje
jeszcze wyraźnego określenia funkcjonalności poszczególnych podsystemów, wystarczająca
okazuje się notacja odzwierciedlająca jedynie powiązania między podsystemami, widoczna na
rysunku 6.4.
Definiowanie podsystemu w kategoriach oferowanych przez niego usług pomaga skupić
się na jego interfejsie, w oderwaniu od konkretnej jego implementacji. Należy przy tym unikać
uzależniania konkretnych elementów interfejsu od szczegółów implementacyjnych, przy-
kładowo definicja operacji przetwarzającej kolekcję danych nie powinna w żaden sposób na-
wiązywać do jakiejkolwiek implementacji tej kolekcji (listy wiązanej, tablicy haszowanej, drzewa
binarnego i tym podobnych); w ten sposób sprawiamy, że wspomniane szczegóły implemen-
tacyjne staja się niewidoczne dla innych podsystemów, a więc nie wywołują żadnych zewnętrz-
nych konsekwencji w przypadku zmiany implementacji.

6.3.3. Sprzężenie i spoistość


Sprzężeniem w zbiorze podsystemów nazywamy stopień ich wzajemnego uzależnienia, mie-
rzony liczbą skojarzeń między nimi. W przypadku małego („luźnego") sprzężenia systemy są
względnie niezależne i zmiana w zakresie jednego z nich ma niewielki wpływa na funkcjono-
wanie pozostałych — i vice versa: duże („ścisłe") sprzężenie wielu podsystemów powoduje, że
stają się one wzajemnie wrażliwe na dokonywane zmiany. Pożądaną cechą dekompozycji jest
tak małe sprzężenie między wynikowymi podsystemami, jakie tylko jest możliwe do osiągnięcia.
272 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Powróćmy jeszcze do przykładu z rysunku 6.4: projektując przedmiotowy system, zde-


cydowaliśmy o przechowywaniu wszystkich danych nieulotnych (czyli przekazywanych między
kolejnymi uruchomieniami systemu) w relacyjnej bazie danych. Prowadzi to do zdefiniowa-
nia nowego podsystemu Database (patrz rysunek 6.6). Początkowo zaprojektowaliśmy jego
interfejs w ten sposób, że systemy żądające przechowywania i udostępniania wspomnianych
danych komunikować się z nim będą za pośrednictwem poleceń w powszechnie używanym,
natywnym języku w rodzaju SQL — i tak na przykład podsystem IncidentManagement, zapi-
sujący w rzeczonej bazie tworzone obiekty Incident i pobierający je stamtąd, będzie to czynił
poprzez kierowanie do podsystemu Database poleceń w rodzaju CREATE i SELECT. Oznacza
to bardzo silne sprzężenie między podsystemem Database a podsystemami będącymi jego
klientami (IncidentManagement, ResourceManagement i MapManagement); jeśli zdecydujemy
się na wymianę systemu zarządzania bazą danych na produkt używający innego dialektu języka
zapytań, będziemy musieli zmienić także sposób komunikowania się trzech wymienionych
podsystemów z podsystemem Database. Aby zredukować opisane uzależnienie, stworzyliśmy
nowy podsystem Storage, pośredniczący w komunikacji między podsystemem Database a jego
dotychczasowymi podsystemami klienckimi. Te ostatnie korzystają teraz z usług oferowanych
przez podsystem Storage, którego interfejs niewrażliwy jest na konkretną implementację
podsystemu Database. Gdy zmieni się serwer bazy danych, a w konsekwencji interfejs pod-
systemu Database, konieczne będą jedynie zmiany w zakresie implementacji podsystemu
Storage, bez wpływu na inne podsystemy korzystające z jego usług. W ten oto sposób zmniej-
szyliśmy znacząco ogólne sprzężenie między podsystemami.
Należy jednak wykazać się ostrożnością: redukowanie sprzężenia podsystemów nie może
odbywać się w oderwaniu od konsekwencji, jakie niesie ze sobą. W przykładzie na rysunku 6.6
redukcja sprzężenia dokonała się za cenę zwiększenia ogólnej komplikacji systemu. Generalnie,
redukowanie sprzężenia z dziką determinacją, za wszelką cenę, objawia się tworzeniem wielu
dodatkowych, niepotrzebnych warstw abstrakcji, nie tylko pochłaniających cenny czas pro-
gramistów, lecz zwykle także odbijających się negatywnie na ogólnej wydajności docelowego
systemu. Duże sprzężenie oznacza wzajemną wrażliwość podsystemów na dokonywane zmiany,
a więc jest problemem tylko wtedy, jeśli zmiany takie rzeczywiście są wysoce prawdopodobne.
Spoistość podsystemu jest miarą uzależnienia jego własnych klas. System złożony z wielu
obiektów, powiązanych wzajemnie i realizujących podobne zadania, jest systemem wysoce
spoistym; system złożony z niewielu obiektów luźno powiązanych (lub niepowiązanych w ogóle)
jest systemem o niskiej spoistości. Pożądaną cechą dekompozycji jest uzyskanie jak najbar-
dziej spoistych podsystemów.
Znaczenie spoistości zilustrujemy na przykładzie systemu śledzenia decyzji, wyposa-
żonego w funkcje rejestrowania problemów projektowych, dokumentowania dyskusji, al-
ternatywnych rozwiązań, decyzji i ich implementowania (patrz rysunek 6.7). Komponenty
Desi gnProbl em i Opti on reprezentują eksplorację przestrzeni projektów: formułujemy system
w kategoriach zbioru problemów projektowych (Desi gnProbl em) i dokumentujemy każdą
opcję (Option) rozwiązywania poszczególnych problemów. Klasa C r i t e r i o n reprezentuje
kryteria różnicowania istotności cech systemu: gdy tylko ocenimy uwzględniane opcje według
zdefiniowanych kryteriów, dokonujemy implementowania podjętych decyzji (Decision)
w formie zadań (Task). Zadania są rekurencyjnie dekomponowane na podzadania (Subtask)
dostatecznie proste, by powierzyć ich realizację pojedynczym programistom; zadanie niepod-
legające dekompozycji („atomowe") reprezentowane jest przez obiekt klasy Acti onltem.
6.3. Koncepcje projektowania systemu 273

Wariant 1: bezpośredni dostęp do podsystemu Database

Wariant 2: podsystem Storage pośredniczy w dostępie do podsystemu Database

Rysunek 6.6. Przykład redukowania sprzężenia w zbiorze podsystemów (dla przejrzystości pominęliśmy
podsystem N o t i f i c a t i o n ) . W wariancie 1. wszystkie podsystemy korzystające z usług podsystemu
Database czynią to bezpośrednio, co uwrażliwia je na zmiany w zakresie interfejsu tegoż podsystemu.
W wariancie 2. podsystem S t o r a g e pełni rolę bariery ochronnej oddzielającej wspomniane systemy
klienckie od interfejsu p o d s y s t e m u Database. U podstaw takiego rozwiązania legło założenie, że
podsystem S t o r a g e cechować się będzie interfejsem daleko stabilniejszym niż podsystem Database

Reprezentowany na rysunku 6.7 system śledzenia decyzji jest na tyle mały, że nie było
niczym nadzwyczajnym zamknięcie wszystkich jego klas w formie pojedynczego podsystemu
DecisionSubsystem. Bliższe przyjrzenie się strukturze powiązań między wspomnianymi
klasami skłania jednak do wniosku, iż graf tych powiązań da się naturalnie podzielić na dwa
podgrafy. Jeden z nich, odpowiadający podsystemowi RationaleSubsystem, wiąże klasy
DesignProblem, Option, Criterion i Decision; drugi, reprezentujący podsystem Planning
'-•Subsystem, zawiera klasy Task, SubTask i Actionltem (patrz rysunek 6.8).
274 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

DecisionSubsystem

Rysunek 6.7. Przykład podsystemu o niskiej spoistości: klasy C r i t e r i o n , Option i DesignProblem


nie mają powiązań z klasami S u b T a s k , Acti onltem i T a s k

Rysunek 6.8. Dekompozycja podsystemu z rysunku 6.7 na dwa odrębne podsystemy. Spoistość wynikowych
podsystemów Rati onal eSubsystem i PI anni ngSubsystem jest znacznie większa niż spoistość pierwotnego
podsystemu Deci si onSubsystem. Oba podsystemy są ponadto prostsze, jednak konieczne jest zdefiniowanie
interfejsu między nimi, w celu zrealizowania powiązań między klasami Task i Deci si on
6.3. Koncepcje projektowania systemu 275

Każdy z podsystemów Rati onal eSubsystem i PlanningSubsystem cechuje się znacznie


większą spoistością niż pierwotny podsystem DecisionSubsystem. Umożliwia to niezależne
wykorzystywanie każdego z nich przez inne podsystemy; co więcej, każdy nich może być bu-
dowany niezależnie przez innego programistę. Są one ze sobą — co prawda — sprzężone, lecz
w minimalnym stopniu, poprzez jedno skojarzenie.
Maksymalna spoistość i minimalne sprzężenie generalnie okazują się celami przeciwstaw-
nymi. Tworzenie nowych, coraz mniejszych i bardziej spoistych podsystemów powoduje zwykle
generowanie nowych powiązań między nimi, a więc przyczynia się do zwiększenia sprzężenia.
Jedną ze znanych heurystyk pomagających rozwiązywać opisany konflikt interesów jest heurystyka
„7 ± 2" zakładająca średnią liczbę siedmiu koncepcji na danym szczeblu abstrakcji: większa niż
dziewięć liczba podsystemów na tym szczeblu bądź większa niż dziewięć liczba usług świadczonych
przez pojedynczy podsystem jest wskazówką w stronę zrewidowania dokonanej dekompozycji.
Zgodnie z tą samą heurystyką, liczba warstw funkcjonalnych systemu nie powinna przekraczać
dziewięciu; w praktyce dobre projekty systemu ograniczają się do nie więcej niż trzech warstw.

6.3.4. Warstwy i partycje


Efektem dekompozycji hierarchicznej jest uporządkowany zbiór warstw. Warstwa stanowi
zgrupowanie podsystemów oferujących powiązane usługi i prawdopodobnie korzystają-
cych z usług oferowanych przez inne warstwy. Warstwy uporządkowane są w ten sposób,
że każda z nich zależna jest co najwyżej od warstw niższych i nie ma żadnej wiedzy na temat te-
go, co dzieje się w wyższych warstwach. Najniższa w hierarchii warstwa, z konieczności nieza-
leżna od innych, nosi nazwę warstwy dolnej, analogicznie warstwa najwyższa w hierarchii
zwana jest warstwą górną lub szczytową (patrz rysunek 6.9)

Rysunek 6.9. Dekompozycja systemu na trzy warstwy (przedstawione jako pakiety UML). Podzbiór
podsystemów, zawierający co najmniej jeden podsystem z każdej warstw)', nazywamy pionowym wycin-
kiem; takim wycinkiem jest podzbiór {A, B, E}, nie jest nim natomiast podzbiór {D, G}

Charakter zależności między warstwami stanowi kryterium podziału architektury dekom-


pozycyjnej na dwa rodzaje. Jeżeli dana warstwa korzysta z usług wyłącznie warstwy bezpośrednio
niższej, mamy do czynienia z architekturą zamkniętą; w ramach architektury otwartej2
każda z warstw może w dowolny sposób wykorzystywać usługi oferowane przez wszystkie
niższe warstwy.

2
W społeczności programistów termin „architektura otwarta" używany jest także w zupełnie innym zna-
czeniu — na określenie architektury sprzętowej lub programowej, niewykazującej cech uzależnienia
od technologii konkretnego dostawcy. W tym miejscu używam tego terminu w znaczeniu identycznym
ze znaczeniem w książce J. Rumbaugha, M. Błahy, W. Premerlaniego, F. Eddy'ego i W. Lorensena
[Rumbaugh i in., 1991],
276 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Klasycznym przykładem architektury zamkniętej jest znany model odniesienia OSI-ISO,


obrazujący koncepcję komunikacji sieciowej między dwiema aplikacjami (patrz rysunek 6.10)
[Day i Zimmermann, 1983]. Każda warstwa odpowiedzialna jest za świadczenie ściśle okre-
ślonych funkcji, każda też (oczywiście, z wyjątkiem najniższej) wykorzystuje usługi warstwy
położonej bezpośrednio poniżej. I tak, idąc od dołu, warstwa f i zyczna reprezentuje sprzętowy
interfejs sieci, odpowiedzialny za transmitowanie pojedynczych bitów przez kanał komunika-
cyjny. Położona bezpośrednio wyżej warstwa łącza danych odpowiedzialna jest za bezbłędne
transmitowanie całych ramek; funkcja transmitowania ramki realizowana jest jako złożenie
prostszych funkcji transmitowania pojedynczych bitów, co „załatwia" nam warstwa fizyczna.
Z kolei funkcja niezawodnego transmitowania ramek stanowi budulec dla zadania bardziej
zaawansowanego — transmitowania i trasowania pakietów sieciowych, za które to zadanie
odpowiedzialna jest warstwa sieciowa. Zadaniem warstwy transportowej jest niezawodne
transmitowanie danych między dwoma połączonymi punktami; programowanie przesyłania
informacji między gniazdami TCP/IP opiera się na usługach tej właśnie warstwy. Zadaniem
warstwy s e s j i jest nawiązywanie i uwierzytelnianie połączeń. Warstwa prezentacji odpo-
wiedzialna jest za niezbędne konwersje przesyłanych danych, polegając między innymi na
szyfrowaniu i odwracaniu kolejności bajtów 3 . Najwyższa warstwa — warstwa apl i kacji
— reprezentuje kompletną aplikację, czyli tworzony system (z wyjątkiem przypadków, gdy
system ten jest na przykład systemem operacyjnym lub implementacją stosu protokołów).
Warstwa aplikacji ma swą wewnętrzną strukturę — realizowana jest przez podsystemy zorga-
nizowane w architekturę warstwową.
Co ciekawe, jeszcze do niedawna tylko cztery najniższe warstwy modelu OSI-ISO były
dobrze zestandaryzowane. System UNIX, podobnie jak większość „desktopowych" systemów
operacyjnych, dostarcza dla protokołu TCP/IP interfejs obejmujący warstwy transportową,
sieciową i łącza danych; implementacja warstw sesji i prezentacji spoczywa całkowicie na bar-
kach programistów tworzących aplikację. W miarę wzrostu popularności aplikacji rozproszo-
nych ten stan rzeczy stał się przesłanką do powstania kilku znaczących rozwiązań z kategorii
middleware, takich jak CORBA [OMG, 2008] czy Java RMI [RMI, 2009] — obie te technologie
znakomicie ułatwiają pracę programistom, pozwalając na wysyłanie komunikatów do zdalnych
obiektów w sposób „przezroczysty", czyli tak samo jak wysyła się komunikaty do lokalnych
obiektów. Z perspektywy modelu OSI-ISO oznacza to efektywną implementację warstwy sesji
i prezentacji (patrz rysunek 6.11).
Przykładem architektury otwartej jest znana biblioteka graficzna Swi ng języka Java [JFC,
2009], a raczej jej umiejscowienie w ogólnym schemacie aplikacji (patrz rysunek 6.12). Najniższa
warstwa tej architektury realizowana jest bądź to przez system operacyjny, bądź przez podsystem
okienkowy w rodzaju Xli, a jej zadaniem jest podstawowe zarządzanie oknami interfejsu użyt-
kownika. Aplikacja w języku Java oddzielona jest od konkretnego systemu okienkowego barierą,
jaką stanowi warstwa AWT (Abstract Window Toolkit). Kolejna warstwa — biblioteka Swi ng —
dostarcza bogaty zestaw obiektów tworzących interfejs użytkownika i realizujących szeroki
wachlarz funkcjonalności, od prostych przycisków do zarządzania geometrią interfejsu.
Aplikacja kliencka może — oczywiście — korzystać z tego bogactwa, równie dobrze może
jednak ignorować bibliotekę Swi ng i odwoływać się bezpośrednio do warstwy AWT.

3
Odwracanie kolejności bajtów w strukturach wielobajtowych ma na celu zniwelowanie różnic między
architekturą little endian a architekturą bigendian (patrz http://pl.wikipedia.org/wiki/Kolejność_bajtów)
— przyp. tłum.
6.3. Koncepcje projektowania systemu 277

Rysunek 6.10. Przykład zamkniętej architektury warstwowej — sieciowy model odniesienia OSI-ISO

Rysunek 6.11. Odwzorowanie warstw modelu OSI-ISO w rzeczywiste komponenty sprzętowe i progra-
mowe. Mechanizm CORBA, stanowiący efektywną implementację warstw sesji i prezentacji, pozwala na
komunikowanie się obiektów rezydujących na różnych komputerach, zaimplementowanych być może
z użyciem odmiennych języków programowania
278 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Rysunek 6.12. Przykład otwartej architektury warstwowej: aplikacja wykonywana w środowisku za-
wierającym bibliotekę Swing. X l i to podsystem odpowiedzialny za niskopoziomowe operacje graficzne;
AWT jest abstrakcją reprezentującą mechanizmy okienkowe i jednocześnie izolującą warstwy wyższe
od konkretnej implementacji tych mechanizmów. Biblioteka Swing udostępnia aplikacji bogaty repertuar
wykoncypowanych obiektów typowych dla interfejsu użytkownika, mimo to, niektóre aplikacje pomijają
tę biblioteką, odwołując się bezpośrednio do usług oferowanych przez warstwę A W T

Podstawową przyczyną, dla której niektóre warstwy architektury otwartej sięgają niżej
niż tyłko do warstwy sąsiedniej, jest niwelowanie wąskich gardeł wydajnościowych. Archi-
tektura zamknięta okazuje się nader korzystna, gdy spojrzeć na nią z perspektywy programistycz-
nej: ponieważ podsystemy należące do różnych warstw są ze sobą bardzo luźno powiązane
(lub niepowiązane w ogóle), czyli ponieważ minimalny jest stopień sprzężenia podsystemów,
mogą być one integrowane i testowane w sposób przyrostowy. Ta wygoda ma jednak swoją
cenę: każdy nowy poziom (warstwa) wnosi do ogólnej architektury zarówno pewne spowol-
nienie wykonywania systemu, jak i dodatkowe zapotrzebowanie na pamięć. Zsumowanie
się tych narzutów może spowodować, że niemożliwe stanie się spełnienie wymagań poza-
funkcyjnych. Ponadto dodawanie nowych elementów funkcjonalnych — zwłaszcza nieprzewi-
dzianych zawczasu — może okazać się trudne w przypadku „głębokiej' architektury. W prakty-
ce, typowy system składa się z najwyżej trzech — wyjątkowo czterech lub pięciu — warstw.
Odmiennym sposobem dekompozycji systemu jest jego partycjonowanie na równo-
rzędne podsystemy, z których każdy odpowiedzialny jest za inną klasę usług. Przykładowo
system pokładowy samochodu może być zdekomponowany na podsystemy zarządzające (od-
powiednio) nawigacją i trasą (w postaci informowania kierowcy na bieżąco o lokalizacji i wy-
maganym kierunku jazdy), personalizacją środowiska (pozycją fotela, ulubioną stacją radiową),
informacją o zużyciu paliwa oraz harmonogramem przeglądów i napraw. Każdy z tych pod-
systemów jest luźno powiązany z pozostałymi, lecz równie dobrze może funkcjonować w ode-
rwaniu od nich.
Dekompozycja przeprowadzana w praktyce ma zwykle charakter pośredni między opi-
sanymi skrajnościami. Dekomponowanie systemu rozpoczyna się najczęściej od jego partycjo-
nowania na wysokopoziomowe podsystemy, z których każdy odpowiedzialny jest za specy-
ficzny aspekt funkcjonalności bądź też przeznaczony do pracy na konkretnym węźle sprzętowym.
Każdy z tych podsystemów może być (o ile uzasadnia to jego złożoność) sukcesywnie dekom-
ponowany w sposób hierarchiczny, aż poszczególne warstwy staną się na tyle proste, że możliwe
6.3. Koncepcje projektowania systemu 279

do zrealizowania przez pojedynczych programistów. Nie zapominajmy jednak o (wspomnia-


nych przed chwilą) narzutach wynikających z przesadnego partycjonowania i o wynikającym
stąd ogólnym wzroście złożoności systemu.

6.3.5. Style architektoniczne


Znaczenie specyfikacji dekompozycyjnej staje się tym bardziej krytyczne, im bardziej skom-
plikowany jest przedmiotowy system. Nietrafną kompozycję koryguje się bardzo trudno, gdy
już rozpocznie się projektowanie i implementowanie systemu, bowiem większość opracowy-
wanych właśnie podsystemów zmieni prawdopodobnie swe oblicze (interfejs) i charakter. Na
kanwie niezwykłej istotności tego problemu zrodziła się koncepcja architektury oprogramo-
wania — pod tym pojęciem rozumiemy dekompozycję systemu, globalną kontrolę przepływu
sterowania, zarządzanie sytuacjami granicznymi oraz protokoły komunikacji między podsys-
temami [Shaw i Garlan, 1996].
W tej sekcji opiszemy kilka stylów architektonicznych, które można wykorzystywać jako
podstawę do projektowania architektury konkretnych systemów. Ów opis ma charakter wy-
biórczy i nie pretenduje do miana systematycznego czy wyczerpującego: chcieliśmy raczej
przedstawić czytelnikom kilka reprezentatywnych przykładów, których uzupełnieniem może
być zalecana literatura.

Repozytorium

W architekturze repozytoryjnej (patrz rysunek 6.13) podsystemy operują na pojedyn-


czej strukturze danych zwanej repozytorium. Repozytorium stanowi jedyne medium komuni-
kacji między podsystemami, które poza tym są od siebie niezależne. Przepływ sterowania
może być determinowany bądź przez wspomniane repozytorium (na przykład w formie wy-
wołań poszczególnych podsystemów, wyzwalanych zmianami w danych), bądź też przez same
podsystemy (których nienależne poczynania synchronizowane są za pomocą blokad nakłada-
nych na poszczególne elementy repozytorium).

Rysunek 6.13. Przykład architektury repozytoryjnej. Każdy podsystem zależny jest jedynie od central-
nego repozytorium, które nie posiada jednak żadnej wiedzy na temat poszczególnych podsystemów

Architektura repozytoryjna używana jest zwykle w systemach skoncentrowanych na


zarządzaniu bazami danych — systemach bankowości, systemach ewidencji pracowników
i tym podobnych. Centralny charakter danych ułatwia radzenie sobie z problemami wynikają-
cymi ze współbieżności i konieczności zachowania integralności danych między podsystemami.
280 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Innym obszarem popularności architektury repozytoryjnej są współczesne kompilatory wbu-


dowane w zintegrowane środowiska programistyczne (patrz rysunek 6.14): poszczególne
podsystemy — debugger, kompletowanie składni, wizualne rozróżnianie elementów skła-
dniowych — korzystają z drzewa rozbioru syntaktycznego i tablicy symboli, produkowanych
przez kompilator.

Rysunek 6.14. Przykład architektury repozytoryjnej — zintegrowane środowisko programistyczne.


Kompilator (Compiler), złożony z analizatora składni ( S y n t a c t i c A n a l y z e r ) , analizatora semantyki
(SemanticAnalyzer), analizatora leksykalnego (LexicalAnalyzer), optymalizatora (Optimizer) i genera-
tora kodu wynikowego (CodeGenerator) produkuje drzewo rozbioru syntaktycznego (ParseTree) i tablicę
symboli (SymbolTable) zapamiętywane w centralnym repozytorium (Repository). Z repozytorium tego
korzysta zarówno zintegrowany debugger (SourceLevel Debugger), jak i edytor programistyczny,
świadom elementów składniowych edytowanego tekstu ( S y n t a c t i cEdi t o r )

W przykładzie z rysunku 6.14 poszczególne narzędzia (kompilator, debugger, edytor)


wywoływane są przez programistę, rola repozytorium ogranicza się jedynie do synchronizo-
wania dostępu do wspólnych danych. Repozytorium może jednak pełnić także rolę czynną,
wywołując poszczególne operacje podsystemów wskutek zmian dokonywanych w centralnej
strukturze danych. Takie systemy nazywane są potocznie „systemami tablicowymi"4 (black-
board systems). Jednym z pierwszych systemów tej kategorii był system rozpoznawania mowy
HEARSAY II, opisany w pracy L. D. Ermana, F. Hayesa-Rotha i innych [Erman i in., 1980].
Architektura repozytoryjna idealnie nadaje się dla aplikacji dokonujących intensyw-
nego przetwarzania dynamicznie zmieniających się danych. Gdy tylko zdefiniowana zostanie
właściwie struktura i działanie centralnego repozytorium, można łatwo definiować nowe usługi
pod postacią nowych podsystemów. Podstawowa wada tej architektury wynika z faktu, że

4
Patrz na przykład http://pl.wikipedia.org/wiki/Architektura_tablicowa — przyp. tłum.
6.3. Koncepcje projektowania systemu 281

centralne repozytorium rychło stać się'może wąskim gardłem i to zarówno pod względem
wydajności, jak i modyfikowalności — poszczególne podsystemy są przecież z tym repozyto-
rium wysoce sprzężone, zatem wszelkie zmiany dokonywane w jego obrębie przekładają się
na konieczność być może daleko posuniętych zmian w samych podsystemach.

Model-widok-kontroler (MVC)

Istotą architektury model-widok-kontroler (w skrócie MVC, od Model-View-Controller),


przedstawionej schematycznie na rysunku 6.15, jest podział podsystemów na trzy grupy, pod-
systemy modelu reprezentują wiedzę z dziedziny aplikacyjnej, podsystemy widoku odpowie-
dzialne są za prezentowanie tej informacji użytkownikowi, zaś zadaniem podsystemów kon-
trolera jest zarządzanie sekwencją interakcji z użytkownikiem. Podsystemy modelu nie są przy
tym w żaden sposób uzależnione od podsystemów dwóch pozostałych grup, zmiany w obrębie
modelu propagowane są do podsystemów widoku za pomocą mechanizmów powiadamiania
i subskrypcji. Architektura MVC może być uważana za szczególny przypadek architektury
repozytoryjnej, model jest tu bowiem odpowiednikiem centralnego repozytorium implemen-
tującego centralne struktury danych, zaś obiekty kontrolera determinują przepływ sterowania.

Rysunek 6.15. Architektura model-widok-kontroler. Kontroler odpowiedzialny jest za interakcje z użyt-


kownikiem i wysyłanie komunikatów do modelu. Model utrzymuje centralną strukturę danych. Widok
realizuje wyświetlanie informacji utrzymywanej przez model — aktualność wyświetlanej informacji
zapewniania jest przez mechanizmy powiadamiania, realizowane przez model w drodze subskrypcji

Na rysunku 6.16 widoczny jest efekt zewnętrzny funkcjonowania architektury MVC.


Okno „w tle" prezentuje listę plików zawartych w folderze Comp-Based Software Engineering
— widzimy tam między innymi plik 9DesignPatterns2.ppt. W oknie pierwszoplanowym wi-
dzimy natomiast szczegółowe informacje na temat tego pliku. Nazwa 9DesignPatterns2.ppt
pojawia się w trzech miejscach: w „spisie treści" folderu w oknie drugoplanowym oraz na pa-
sku tytułowym i w nagłówku okna pierwszoplanowego. Zobaczmy teraz, co wydarzy się (po-
winno się wydarzyć?) w tym systemie, gdy zmienimy nazwę rzeczonego pliku — diagram na
rysunku 6.17 przedstawia sekwencję związanych z tym zdarzeń:

1. Obiekty InfoView i FolderView subskrybują powiadamianie o zmianach zacho-


dzących w Model u5.
2. Użytkownik wprowadza nową nazwę pliku.

3
Ten krok wykonywany jest — oczywiście — jednorazowo, prawdopodobnie w fazie inicjacji systemu
— przyp. tłum.
282 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Rysunek 6.16. Przykład zastosowania architektury MVC. „Modelem" jest tu nazwa pliku 9Design-
PAtterns2.ppt, powiązana z dwoma „widokami": oknem zatytułowanym CBSE, wyświedającym zawartość
folderu, i oknem pierwszoplanowym, wyświetlającym właściwości pliku. Gdy zmieni się nazwa pliku,
oba widoki zostaną uaktualnione na skutek interwencji „kontrolera"

Rysunek 6.17. Diagram komunikacyjny ukazujący przepływ informacji w związku ze zmianą nazwy
pliku w systemie pokazanym na rysunku 6.16

3. Obiekt Kontroler, odbierając od użytkownika wprowadzoną nazwę, przekazuje


do Model u żądanie zrealizowania zmiany nazwy pliku.
4. Obiekty modelu dokonują zmiany nazwy pliku i powiadamiają o tym swych sub-
skrybentów — obiekty InfoView i FolderView.
5. Obiekty InfoView i FolderView uaktualniają wyświetianą informację, użytkownik
widzi więc aktualny stan rzeczy.
6.3. Koncepcje projektowania systemu 283

Występujące w sekwencji na rysunku 6.17 mechanizmy subskrypcji i powiadamiania


realizowane są zwykle w oparciu o wzorzec projektowy Obserwator (patrz sekcja A.7), co
przyczynia się do ogólnego obniżenia stopnia sprzężenia podsystemów, brakuje bowiem ja-
kichkolwiek bezpośrednich powiązań modelu z widokami. Czytelnikom zainteresowanych
tematyką wzorców projektowych, a szczególnie wzorca Obserwator, możemy polecić książkę
E. Gammy, R. Heima, R. Johnsona i J. Vlissidesa [Gamma i in., 1994]6.
Jedną z istotnych przesłanek uzasadniających rozseparowanie obiektów modelu, widoku
i kontrolera jest fakt, iż interfejs użytkownika (realizowany przez obiekty widoku i kontrolera)
jest z natury bardziej podatny na zmiany niż (reprezentowana przez model) dziedzina aplika-
cyjna. Ponadto izolując model od widoku, zapewniamy sobie swobodę manipulowania pod-
systemami widoku bez jakiegokolwiek wpływu na podsystemy modelu — i tak na przykład
możemy w systemie operacyjnym uruchamiać rozmaite przeglądarki ukazujące żądane szcze-
góły systemu plików, bez konieczności jakiejkolwiek ingerencji w ów system plików7. Czytelnicy
przypominają sobie zapewne analogiczną separację obiektów encji, brzegowych i sterujących,
którą opisywaliśmy w rozdziale 5. „Analiza wymagań" — jest ona motywowana dokładnie
tymi samymi przesłankami.
Architektura MVC jest znakomicie przystosowana do systemów interaktywnych, szcze-
gólnie takich, w których informacje zawarte w pojedynczym modelu wyświetlane są w wielu
widokach. Pomaga ona w zapewnieniu spójności rozproszonych danych, dotknięta jest jednak
tym samym syndromem „wąskiego gardła", co architektura repozytoryjna.

Klient-server

W ramach tej architektury (patrz rysunek 6.18) podsystem zwany serwerem jest dostawcą
usług dla (jednej lub wielu) instancji innych podsystemów, zwanych klientami i odpowiedzial-
nych za interakcję z użytkownikiem (użytkownikami). Żądanie usługi odbywa się zwykle za
pośrednictwem zdalnego wywoływania procedur (RPC — Remote Procedure Calif bądź za po-
średnictwem obiektów brokerów (jak w przypadku technologii CORBA, Java RMI czy proto-
kołu HTTP). Podsystemy klienckie funkcjonują niezależnie od podsystemu serwera, z wyjąt-
kiem synchronizacji niezbędnej do zarządzania żądaniami klientów i odpowiedziami serwera.
Przykładem zastosowania architektury klient-serwer jest system informacyjny oparty
na centralnej bazie danych. Każdy podsystem klienta odpowiedzialny jest za obsługę dialogu
z użytkownikiem, walidację wprowadzanych przez niego danych i inicjowanie transakcji ba-
zodanowych, gdy wynik tej walidacji okaże się pozytywny. Serwer jest natomiast odpowiedzialny
za wykonywanie żądanych transakcji i gwarantowanie integralności danych. W tym kontekście

6
A także książkę Head First Design Patterns. Edycja polska (Rusz głową!), wyd. Helion 2005,
(http://helion.pl/ksiazki/head_Jirst_design _patterns_edycja _polska_rusz_glowa_eric_jreeman_elisabeth_
freeman_kathy_sierra_bert_bates,hfdepa.htm) —przyp. tłum.
1
Mechanizmy powiadamiania o zmianach zachodzących w systemie plików udostępniane są przez systemy
operacyjne na zasadzie subskrypcji za pomocą odpowiednich funkcji API, przykładowo w systemie
Windows temu celowi służą między innymi funkcje Win32 API Fi ndFi rstChangeNoti f i c a t i on
i FindNextChangeNotifi c a t i o n — p r z y p . tłum.
8
Lub zaawansowanych mechanizmów zbudowanych na bazie RPC, takich jak COM, D C O M czy
C O M + firmy Microsoft — przyp. tłum.
284 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Rysunek 6.18. Architektura klient-serwer. Klienci żądają usług od jednego lub większej liczby ser-
werów. Serwery nie dysponują żadną informacją na temat klientów. Architektura klient-serwer mo-
że być uważana za specjalizację architektury repozytoryjnej

serwer może być uważany za szczególny przypadek repozytorium (realizowanego jako proces
zarządzający centralnymi danymi), zaś architektura klient-serwer — za szczególny przypadek
(specjalizację) architektury repozytoryjnej. Systemy klient-serwer mogą jednak wykorzy-
stywać wiele serwerów — czego ewidentnym przykładem jest choćby sieć WWW, w której
każdy klient może z łatwością pobierać dane z wielu tysięcy serwerów (patrz rysunek 6.19).

Rysunek 6.19. Sieć W W W jako instancja architektury klient-serwer

Architektura klient-serwer jest doskonale przystosowana do systemów rozproszo-


nych zarządzających olbrzymimi zasobami danych.

Peer-to-peer

Styl architektoniczny peer-to-peer 9 (patrz rysunek 6.20), w skrócie P2P, może być
uważany za uogólnienie (generalizację) architektury klient-serwer, bowiem każdy z podsys-
temów spełniać może obie funkcje: klienta i serwera — w tym sensie, że może zarówno żądać
usług, jak i sam je udostępniać. Funkcjonowanie poszczególnych podsystemów jest niezależne,
z wyjątkiem synchronizacji koniecznej dla obsługi żądań.
Przykładem architektury P2P jest system wykorzystujący bazę danych, która zarówno
realizować może żądania związane z odczytem, wyszukiwaniem i modyfikowaniem danych,
jak i powiadamiać aplikację o dokonywaniu zmian w pewnych obszarach danych (patrz rysu-
nek 6.21). Systemy P2P są trudniejsze do projektowania niż systemy utrzymane w architektu-
rze klient-serwer, bo cechują się bardziej skomplikowanym przepływem sterowania i stwarzają
zagrożenie wystąpienia niepożądanych zjawisk w rodzaju zastoju (deadlock).

' Od ang. „równy z równym" — przyp. tłum.


6.3. Koncepcje projektowania systemu 285

konsument
Peer
*

usługalO
usługa2()
dostawca
usługaNO

Rysunek 6.20. Architektura peer-to-peer (P2P). Każdy podsystem może spełniać funkcje zarówno
klienta, jak i serwera

Rysunek 6.21. Przykład architektury P2P. Serwer zarządzania bazą danych (DBMS — Database Mana-
gement System) może zarówno realizować żądania otrzymywane od klienta, jak i powiadamiać go
o zaistniałych zdarzeniach

Z systemami P2P wiąże się mechanizm odwołania zwrotnego (callback). Klient może
mianowicie wskazać serwerowi jedną ze swych operacji i zażądać, by serwer wywołał tę ope-
rację w przypadku wystąpienia określonego zdarzenia. W przykładzie pokazanym na rysunku
6.21 klient DBUser wykorzystuje tę możliwość w stosunku do serwera bazy danych DBMS.
Systemy P2P, w których „serwery" odwołują się do „klientów" jedynie za pośrednictwem od-
wołań zwrotnych, utożsamiane są z systemami klient-serwer — co jest o tyle nieadekwatne,
że w tym przypadku połączenie między klientem a serwerem może być nawiązywane także
z inicjatywy serwera.

Architektura trójwarstwowa

Architektura trójwarstwowa jest wynikiem organizacji systemu w postaci trzech warstw


(patrz rysunek 6.22):

• interfejsu — warstwa ta grupuje wszystkie obiekty brzegowe uczestniczące w interakcji


z użytkownikiem: okna, formularze, strony W W W i tym podobne,
• logiki aplikacji — w tej warstwie zlokalizowane są wszystkie obiekty encji i obiekty
sterujące, realizujące przetwarzanie danych, wymuszanie reguł tego przetwarzania
i powiadamianie aplikacji o wybranych zdarzeniach,
• magazynowania danych — ta warstwa odpowiedzialna jest za przechowywanie,
wyszukiwanie i modyfikowanie obiektów trwałych.
286 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Rysunek 6.22. Architektura trójwarstwowa: obiekty zorganizowane są w trzy warstwy realizujące


interfejs, przetwarzanie danych i ich magazynowanie

Architektura trójwarstwowa wynaleziona została w latach 70. ubiegłego wieku na po-


trzeby systemów informacyjnych. Warstwa magazynowania danych, jako analogia repozyto-
rium, może być współdzielona między wiele aplikacji operujących na tych samych danych.
Z kolei odseparowanie warstwy interfejsu od warstwy logiki biznesowej umożliwia wykorzy-
stywanie różnych interfejsów w oparciu o tę samą logikę aplikacji.

Architektura czterowarstwowa

Architektura czterowarstwowa jest wynikiem modyfikacji architektury trójwarstwowej


polegającej na podziale warstwy interfejsu na dwie nowe: serwer prezentacji i klienta pre-
zentacji (patrz rysunek 6.23). Warstwa klienta prezentacji realizowana jest na maszynach
klienckich, podczas gdy warstwa serwera prezentacji zlokalizowana jest na jednym lub kilku
serwerach. Przez podział warstwy interfejsu na dwie warstwy prezentacyjne stwarza się moż-
liwość zastosowania różnych prezentacji interfejsu wykorzystujących te same obiekty. Przy-
kładowo system bankowości używany jest przez wielu klientów, posługujących się różnymi
interfejsami: przeglądarką WWW, jaką wykorzystuje klient realizujący przelew z domowego
komputera, pulpitem bankomatu czy dedykowaną aplikacją, za pomocą której uprawnieni
pracownicy banku zarządzają opisywanym systemem. Współdzielone przez każdy z tych
interfejsów obiekty (na przykład formularze) mogą być zlokalizowane we wspólnej war-
stwie serwera prezentacji, co przyczynia się do zmniejszenia redundancji systemu.

Filtry i potoki

W opisywanej architekturze (patrz rysunek 6.24) każdy z podsystemów realizuje prze-


twarzanie danych otrzymanych od innych podsystemów na swym wejściu i wysyła wyjście
wyniki tego przetwarzania przeznaczone dla innych podsystemów. Każdy z podsystemów
nazywany jest w tej strukturze filtrem, zaś połączenia między podsystemami to potoki. Dla
każdego z filtrów jedynym źródłem informacji są dane obecne w jego potoku wejściowym
— nie wie on nic na temat podsystemów produkujących te dane. Poszczególne filtry działają
niezależnie, z wyjątkiem synchronizacji związanej z przekazywaniem danych przez potoki.
6.3. Koncepcje projektowania systemu 287

Rysunek 6.23. Architektura czterowarstwowa: warstwa interfejsu z architektury trójwarstwowej po-


dzielona została na dwie, co stworzyło większe możliwości różnicowania stylów interfejsu użytkownika

wyjście

Rysunek 6.24. Architektura filtr-potok. Każdy filtr może posiadać wiele wejść i wyjść, każdy potok
natomiast łączy dokładnie dwa filtry

Architektura filtr-potok jest bardzo elastyczna — poszczególne filtry można łatwo wy-
mieniać i modyfikować, stosownie do bieżących potrzeb.
Najbardziej spektakularnym przykładem obecności tej architektury jest powłoka
systemu UNIX opisana przez D. M. Ritchie i K. Thompsona [Ritchie i Thompson, 1974]l0.
Większość filtrów tworzona jest w ten sposób, że dane pobierane są ze standardowych
potoków wejściowych i wysyłane na standardowe potoki wyjściowe, co umożliwia łatwe or-
ganizowanie zaawansowanego przetwarzania. Na rysunku 6.25 widzimy uniksową sekwencję
filtrów prowadzącą do czytelnego wyświetlenia listy procesów należących do użytkownika
d u t o i t . Najpierw za pomocą polecenia ps uzyskujemy listę wszystkich procesów; program
grep eliminuje z tej listy pozycje związane z innymi użytkownikami, program s o r t sortuje
wynik tej eliminacji w żądanej kolejności, zaś program more organizuje wyświetlanie posor-
towanej listy w porcjach dostosowanych do wysokości ekranu terminala.
Cechą charakterystyczną (i podstawową zaletą) architektury filtr-potok jest jej automa-
tyzm — skomplikowane przetwarzanie informacji może odbywać się bez ingerencji użytkownika.
Architektura ta nie nadaje się jednak do zastosowań wymagających bardziej zaawansowanych
interakcji między komponentami — systemów informacyjnych czy systemów interaktywnych.

10
Analogiczny mechanizm istnieje także w systemie Windows. Jego pierwowzór istniał już nawet
w systemie MS DOS, z naturalnym dla tego systemu ograniczeniem — poszczególne filtry uruchamiane
były sekwencyjnie. W danej chwili aktywny był więc dokładnie jeden filtr, wykorzystujący dwa potoki
— standardowe wejście i standardowe wyjście, oba zrealizowane jako pliki tymczasowe tworzone i usu-
wane przez system — przyp. tłum.
288 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

X ps auxwww | grep dutoit | sort | more


dutoit 19737 0.2 1.6 1908 1500 pts/6 0 15:24:36 0:00 -tcsh
dutoit 19858 0.2 0.7 816 580 pts/6 S 15:38:46 0:00 grep dutoit
dutoit 19859 0.2 0.6 812 540 pts/6 0 15:38:47 0:00 sort

Rysunek 6.25. Sekwencja poleceń uniksowych zrealizowana jako kombinacja filtrów i potoków

6.4. Aktywności projektowania systemu:


od obiektów do podsystemów
Projektowanie systemu to transformowanie modelu analitycznego w model projektu sys-
temu, z uwzględnieniem wymagań pozafunkcyjnych opisanych w dokumencie RAD. Zilu-
strujemy związane z tym aktywności na przykładzie systemu MyTri p, służącego kierowcom
samochodów do planowania podróży. Rozpoczniemy od modelu analitycznego (patrz sekcja
6.4.1), na podstawie którego określimy cele projektowe (patrz sekcja 6.4.2), po czym przystą-
pimy do wstępnej dekompozycji systemu (patrz sekcja 6.4.3).

6.4.1. Punkt wyjścia: model analityczny systemu planowania podróży


Wykorzystując system MyTri p, kierowca może zaplanować trasę podróży za pomocą domowego
komputera, połączonego z odpowiednią usługą internetową (w tabeli 6.2 nazwaliśmy ją Plan
^ T r i p). Wynik planowania przechowywany jest na serwerze do późniejszego wykorzystywania.
System MyTri p musi być zdolny do obsługi wielu klientów równocześnie.

Tabela 6.2. Przypadek użycia PI anTri p systemu MyTri p

Nazwa przypadku użycia PI anTri p

Przepływ zdarzeń 1. Kierowca włącza domowy komputer i loguje się do internetowej


usługi planowania podróży.
2. Kierowca wprowadza ograniczenia w postaci docelowego miejsca
podróży oraz miejsc, które chce odwiedzić po drodze.
3. Bazując na dostępnych mapach, usługa planowania dokonuje ustalenia
najkrótszej drogi spełniającej podane ograniczenia, czyli zawierającej
wszystkie wymienione lokalizacje w ustalonej kolejności.
4. Kierowca może zmodyfikować z a p r o p o n o w a n ą trasę, d o d a j ą c
do niej lub usuwając z niej żądane lokalizacje.
5. Kierowca zapamiętuje w bazie danych usługi zaplanowaną trasę
w celu późniejszego jej wykorzystania.

W dogodnym dla siebie czasie kierowca wsiada do samochodu i rozpoczyna podróż,


podczas której komputer pokładowy wskazuje mu kierunek jazdy, bazując na zaplanowanej
trasie, pobranej z bazy danych usługi oraz na bieżącym położeniu wskazywanym przez pokłado-
wy system GPS (przypadek użycia ExecuteTri p — patrz tabela 6.3).
6.4. Aktywności projektowania systemu: od obiektów do podsystemów 289

Tabela 6.3. Przypadek użycia ExecuteTri p systemu MyTri p

Nazwa przypadku użycia ExecuteTri p

Przepływ zdarzeń 1. Kierowca uruchamia swój samochód i loguje się do pokładowego


asystenta kierowcy.
2. Po pomyślnym zalogowaniu kierowca wybiera żądaną usługę i nazwę
zapamiętanej trasy.
3. Pokładowy asystent kierowcy wczytuje listę lokalizacji, kierunków,
segmentów i skrzyżowań znajdujących się na wspomnianej trasie.
4. Bazując na bieżącej lokalizacji, pokładowy asystent wskazuje kierowcy
ciąg kolejnych kierunków jazdy.
5. Kierowca przybywa do celu podróży i wyłącza pokładowego asystenta.

Dokonując analizy systemu MyTrip za pomocą technik opisanych w rozdziale 5.


„Analiza wymagań", otrzymujemy model analityczny widoczny na rysunku 6.26. Znaczenie jego
poszczególnych klas opisane jest w tabeli 6.4.

Rysunek 6.26. Model analityczny systemu planowania i realizowania podróży MyTri p

Tabela 6.4. Znaczenie klas modelu analitycznego systemu MyTri p, przedstawionych na rysunku 6.26

Klasa Reprezentowana encja


Crossing Skrzyżowanie, punkt geograficzny, w którym spotyka się kilka segmentów (Segment).
Destination Cel podróży, lokalizacja końca podróży.
Direction Dla danego skrzyżowania (Crossi ng) i przylegającego doń segmentu (Segment)
stanowi opis słowny sposobu skierowania samochodu w ten właśnie segment.
Location Lokalizacja samochodu określona na podstawie wskazań pokładowego
systemu i odległości (mierzonej liczbą obrotów kół) od punktu wzorcowego.
PlanningService Planista, serwer W W W , ustalający trasę podróży przez konwersję ciągu
lokalizacji do postaci ciągu skrzyżowań (Crossi ng) i segmentów (Segment).
RouteAssi stant Asystent kierowcy, urządzenie wskazujące kierowcy właściwy kierunek jazdy na
podstawie bieżącej lokalizacji (Location) i następnego skrzyżowania (Crossi ng).
Segment Droga łącząca dwa skrzyżowania (Crossi ng).
Trip Trasa, ciąg kierunków ( D i r e c t i o n ) między dwiema lokalizacjami (Location).
290 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Ponadto na etapie zbierania wymagań ustaliliśmy z klientem następujące wymagania


pozafunkcyjne.

Wymagania pozafunkcyjne dla systemu MyTrip


1. Kontakt z serwerem systemu (PI anni ngServi ce) powinien odbywać się przez m o d e m
bezprzewodowy. Zakładamy, że jakość łączności jest zadowalająca w początkowej lokalizacji
podróży.
2. Gdy podróż zostanie rozpoczęta, MyTri p powinien wskazywać właściwe kierunki nawet
w przypadku braku połączenia z planistą (PI anni ngServi ce).
3. Łączny czas połączenia z planistą powinien być jak najmniejszy, ze względu na koszty.
4. Zmiana planu podróży powinna być możliwa w warunkach połączenia z planistą.
5. Planista powinien być zdolny do równoczesnej obsługi przynajmniej 50 klientów i 1000 tras.

6.4.2. Identyfikowanie celów projektowych


Definiowanie celów projektowych jest pierwszym krokiem projektowania systemu. Obej-
muje ono określenie najważniejszych cech systemu. Wiele celów projektowych można wy-
prowadzić wprost z wymagań pozafunkcyjnych lub z dziedziny aplikacyjnej, inne ustalane są
w drodze negocjacji z klientem. Niezależnie jednak od genealogii, wszystkie cele projektowe
powinny być wyraźnie udokumentowane, tak by wszystkie istotne decyzje podejmowane były
w sposób spójny, czyli w oparciu o ten sam zestaw kryteriów.
I tak na przykład w świetle wymagań pozafunkcyjnych, opisanych w sekcji 6.4.1, może-
my zidentyfikować dwie cechy systemu MyTri p o randze celów projektowych — niezawodność
oraz zdolność funkcjonowania w warunkach utraty łączności. Jako że z systemu korzystać
może równocześnie wielu klientów, identyfikujemy następny cel — bezpieczeństwo, jako
ochronę danych klienta przed ingerencją (zamierzoną łub nie) ze strony innych klientów.
Klient (kierowca) powinien mieć także możliwość wyboru konkretnej usługi planowania,
co identyfikujemy jako kolejny cel — modyfikowalność. W poniższej ramce przedstawiamy
wyraźne sformułowanie wymienionych celów.

Cele projektowe systemu MyTri p


® Niezawodność: MyTri p powinien funkcjonować niezawodnie [generalizacja wymagania
pozafunkcyjnego nr 2].
® Odporność na awarie: MyTri p powinien tolerować utratę połączenia z serwerem [inna postać
wymagania pozafunkcyjnego nr 2].
• Bezpieczeństwo: MyTri p powinien być bezpieczny, czyli nie powinien udostępniać danych
użytkownika innym użytkownikom, nie powinien też zezwalać na nieautoryzowany dostęp
[wynik dedukcji z dziedziny aplikacyjnej],

• Modyfikowalność: MyTri p powinien być modyfikowalny, czyli musi umożliwiać wybór między
różnymi usługami planowania podróży [wynik przewidywania przyszłych potrzeb klienta].

Ogólnie cele projektowe wybierane są z długiej listy najbardziej pożądanych cech syste-
mu. W tabelach od 6.5 do 6.9 przedstawiamy listy grupujące możliwe kryteria projektowania.
Kryteria te zorganizowaliśmy w pięć grup, związanych z (kolejno) wydajnością, pewnością
6.4. Aktywności projektowania systemu: od obiektów do podsystemów 291

działania, kosztami, pielęgnowalnością i kryteriami użytkownika. Wydajność, niezawodność


i kryteria użytkownika określane są zwykle w specyfikacji wymagań bądź dają się wydeduko-
wać z dziedziny aplikacyjnej. Kryteria kosztowe i pielęgnacyjne określane są w drodze nego-
cjacji klienta z dostawcą systemu.
Kryteria wydajności (tabela 6.5) obejmują wymagania dotyczące szybkości działania
i zapotrzebowania na pamięć. Czy system powinien być raczej wysoce reaktywny, czy może
ważniejsze jest, by realizował równocześnie jak najwięcej zadań? Czy możliwe jest zwiększone
wykorzystanie pamięci w celu zwiększenia szybkości działania, czy też pamięć powinna być
raczej oszczędnie używana?

Tabela 6.5. Kryteria dotyczące wydajności

Kryterium projektowe Definicja


Czas reakcji Jak długo użytkownik musi oczekiwać na odpowiedź systemu po
wysłaniu żądania?

Przepustowość Ile zadań system zdolny jest wykonać w ustalonym przedziale czasu?

Pamięć Jak dużo pamięci potrzebuje system do działania?

Kryteria pewności działania (tabela 6.6) związane są z wysiłkiem, jaki należy zain-
westować w minimalizowanie zarówno prawdopodobieństwa awarii systemu, jak i konse-
kwencji ewentualnych awarii. Jak często system może się załamywać? W jakim stopniu
powinien być dostępny dla użytkowników? W jakim zakresie powinien tolerować awarie
i konsekwencje swych własnych błędów? Czy użytkowanie systemu pociąga za sobą zagro-
żenia dla środowiska? Jeśli tak, to jakie ? Czy awaria systemu może zagrażać bezpieczeń-
stwu jego użytkowników?

Tabela 6.6. Kryteria dotyczące pewności działania

Kryterium projektowe Definicja


Solidność Zdolność obsługi nieprawidłowych danych wejściowych.

Niezawodność Różnica między specyfikowanym a rzeczywistym zachowaniem.

Dostępność W jakim procentowo czasie system może być używany do spełniania


swych normalnych zadań?

Odporność Zdolność do działania w nienormalnych warunkach.

Zabezpieczenia Zdolność powstrzymywania ataków z zewnątrz.

Bezpieczeństwo Brak zagrożenia dla ludzkiego życia i zdrowia, nawet w warunkach


błędów i usterek.

Kryteria kosztowe (tabela 6.7) związane są z kosztami opracowania, wdrożenia i admi-


nistracji systemu. Zauważmy, że są wśród tych kryteriów takie, które nie mają bezpośred-
niego związku z projektowaniem systemu, lecz dotyczą raczej menedżerskich aspektów całego
projektu. Gdy nowy system ma stać się następcą istniejącego systemu, powstaje problem ob-
sługi istniejących danych w nowym środowisku — problem, który rozwiązać można dwojako:
poprzez zapewnienie kompatybilności wstecz w nowym systemie albo przez konwersję
292 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Tabela 6.7. Kryteria dotyczące kosztów

Kryterium projektowe Definicja


Koszty projektowania Koszty zaprojektowania wstępnej wersji systemu.

Koszty wdrożenia Koszt zainstalowania systemu i przeszkolenia użytkowników.

Koszty aktualizacji Koszt konwersji danych, zależny od wymagań dotyczących


zachowania kompatybilności wstecz.

Koszty pielęgnacji Koszty usuwania błędów i rozbudowywania systemu.

Koszty administracyjne Koszty administrowania systemem.

danych do nowej postaci, wymaganej przez nowy system (bądź za pomocą innej procedury
o charakterze pośrednim między wymienionymi skrajnościami). Nie można także zapominać
0 kosztach szkolenia użytkowników nowego systemu i kosztach jego pielęgnowania.

Kryteria pielęgnowalności (tabela 6.8) determinują stopień trudności utrzymywania


1 rozwijania systemu po wdrożeniu. Jak łatwo można poszerzać system o nowe elementy
funkcjonalne? Jak łatwo można modyfikować jego obecne funkcje? Czy możliwe jest łatwe za-
adaptowanie systemu do innej dziedziny aplikacyjnej? Jak skomplikowane może być przeno-
szenie systemu na inną platformę sprzętową lub programową? Kryteria tej grupy bardzo trudno
planować i optymalizować, bowiem trudno zawczasu przewidzieć zarówno przebieg realizacji
projektu, jak i okres eksploatowania nowego systemu.

Tabela 6.8. Kryteria dotyczące pielęgnowalności

Kryterium projektowe Definicja


Rozszerzalność Jak łatwo można dodawać nowe klasy i nowe elementy funkcjonalne
do systemu?

Modyfikowalność Jak łatwo można zmieniać funkcje aktualnie realizowane przez system?

Adaptowalność Jak łatwo można przystosować system do innej dziedziny aplikacyjnej?

Przenośność Jak łatwo można przystosować system do działania na innej platformie


sprzętowej lub programowej?

Czytelność Jak zrozumiały okazuje się system na podstawie studiowania jego


kodu źródłowego?

Identyfikowalność Jak wyraźne jest odwzorowanie poszczególnych fragmentów kodu


wymagań źródłowego systemu w poszczególne wymagania zapisane w specyfikacji
wymagań?

Kryteria użytkownika (tabela 6.9) obejmują te cechy systemu, które istotne są z per-
spektywy jego użytkownika, lecz nie zostały ujęte w kategoriach wydajności ani pewności
działania. Jak trudna jest nauka obsługi systemu? Czy typowy użytkownik zdolny będzie do
bezbłędnego wykonywania powierzonych mu zadań? Często zdarza się, że pytań tego rodzaju
nie traktuje się z należytą uwagą — zwłaszcza wówczas, gdy klient kontraktujący system
nie jest jego bezpośrednim użytkownikiem.
6.4. Aktywności projektowania systemu: od obiektów do podsystemów 293

Tabela 6.9. Kryteria specyficzne dla użytkownika systemu

Kryterium projektowe Definicja

Przydatność W jakim stopniu system zdolny jest wspomagać użytkownika w jego


codziennej pracy?

Użyteczność Jak łatwe jest dla użytkownika korzystanie z systemu?

D e f i n i u j ą c cele p r o j e k t o w e , r o z p a t r u j e się j e d n o r a z o w o niewielkie g r u p y w y m i e n i o n y c h


k r y t e r i ó w , rozstrzygając n i e u c h r o n n e k o m p r o m i s y m i ę d z y n i m i : nie jest n a p r z y k ł a d r e a l n e
stworzenie o p r o g r a m o w a n i a n i e z a w o d n e g o , bezpiecznego i j e d n o c z e ś n i e taniego. Programiści,
d e c y d u j ą c się n a o k r e ś l o n y w y b ó r celów p r o j e k t o w y c h , p o w i n n i k o n f r o n t o w a ć je także z czyn-
nikami o charakterze menedżerskim, takimi jak ograniczenia budżetowe i wynikające z har-
m o n o g r a m u . W tabeli 6.10 p r z e d s t a w i a m y kilka w y b r a n y c h p r z y k ł a d ó w w s p o m n i a n y c h
kompromisów.

Kompromis Przesłanki r o z s t r z y g a n i a

Szybkość kontra Jeśli aplikacja nie spełnia wymagań pod względem maksymalnego czasu
zapotrzebowanie na reakcji czy minimalnej przepustowości, można przyspieszyć jej działanie,
pamięć stosując na przykład cache'owanie, co wymaga dodatkowej pamięci. Jeśli
aplikacja jest zbyt pamięciożerna, można zmniejszyć zapotrzebowanie na
pamięć, przykładowo przez kompresję danych czy wielokrotne obliczanie
tych samych wielkości zamiast ich zapamiętywania — co oczywiście
wymaga dodatkowego czasu procesora.

Czas dostarczenia Gdy dotrzymanie terminów określonych w harmonogramie staje się


kontra zakres zagrożone, menedżer może zdecydować o okrojeniu zakresu fiinkcjonałnego
funkcjonalny systemu w celu przyspieszenia realizacji projektu bądź też o zachowaniu
zakładanej funkcjonalności systemu kosztem jego spóźnionego dostarczenia.
Dla systemów kontraktowanych funkcjonalność systemu staje się w takiej
sytuacji czynnikiem ważniejszym niż jego t e r m i n o w e ukończenie, dla
oprogramowania „z półki" ważniejsza jest zwykle terminowość dostarczenia.

Czas dostarczenia Jeśli terminowe dostarczenie systemu zagrożone jest z powodu trwających
kontra jakość testów systemu, m e n e d ż e r może zdecydować o dostarczeniu systemu
n i e k o m p l e t n i e przetestowanego lub zawierającego r o z p o z n a n e błędy
(dła których w terminie późniejszym przewidziane są łaty łub poprawki)
albo dostarczyć z opóźnieniem system lepiej przetestowany, po usunięciu
znanych błędów.

Czas dostarczenia Gdy terminowość realizacji poszczególnych części systemu staje się zagrożona,
kontra siły menedżer projektu może zdecydować o zaangażowaniu w projekt kolejnych
wytwórcze uczestników. Zwiększenie liczebności personelu ma jednak pozytywne skutki
raczej we wczesnych stadiach realizacji projektu, bowiem w późniejszych
fazach nowi uczestnicy wymagają przeszkolenia wiążącego się z czasowo
obniżoną produktywnością. Niezależnie od pozytywnych efektów, przydział
dodatkowych zasobów do projektu zawsze wiąże się z dodatkowymi kosztami.
294 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Jak widać, cele menedżerskie mogą kolidować z celami technicznymi — przykładowo


bezwzględnie terminowe dostarczenie systemu może odbyć się kosztem jego funkcjonalności.
Mając już jasno zdefiniowane cele projektowe, możemy przystąpić do wstępnego dekompo-
nowania systemu.

6.4.3. Identyfikowanie podsystemów


Identyfikowanie podsystemów na etapie projektowania systemu jest podobne do identyfi-
kowania obiektów modelu analitycznego — można w tym celu wykorzystywać niektóre techniki
opisane w rozdziale 5. „Analiza wymagań", na przykład heurystyki Abbotta. Identyfikowanie
podsystemów jest procesem ewoluującym w miarę rozpoznawania nowych okoliczności: nie-
które podsystemy zostają łączone w większe całości, złożone podsystemy dzielone są na prostsze
części, pojawiają się także nowe podsystemy odzwierciedlające nowo zidentyfikowaną funk-
cjonalność. Pierwsze iteracje dekomponowania systemu, przeprowadzane zwykle w formie
„burzy mózgów", objawiają się drastycznymi zmianami w jego modelu.
Początkowo dekompozycja systemu powinna odbywać się w oparciu o jego wymagania
funkcjonalne. Przykładowo w systemie MyTri p możemy wyróżnić dwie grupy obiektów: te, które
uczestniczą w przypadku użycia PI anTri p, i te uwikłane w przypadek użycia ExecuteTri p.
Obiekty Tri p, Di r e c t i on, Crossi ng, Segment i Desti nati on wspólne są dla obu przypadków;
są one ściśle powiązane i jako całość mogą być uważane za reprezentację podróży. Dodając im
do towarzystwa obiekt PI anni ngServi ce, zamykamy całość w odrębny podsystem służący pla-
nowaniu podróży, nazwany PI anni ngSubsystem; pozostałe obiekty — Location i Route
^•Assi s t a n t — zamykamy w drugi podsystem Routi ngSubsystem, służący do wskazywa-
nia kierowcy trasy podróży. Między wyodrębnionymi podsystemami występuje tylko jedno
skojarzenie. Wynik tej dekompozycji widoczny jest na rysunku 6.27, funkcje obu podsyste-
mów opisane zostały w tabeli 6.11. Rozpoznawalna jest analogia repozytorium, jaką jest pod-
system PI anni ngSubsystem zarządzający centralną strukturą danych.

Rysunek 6.27. Wynik wstępnej dekompozycji systemu MyTri p


6.4. Aktywności projektowania systemu: od obiektów do podsystemów 295

Tabela 6.11. Funkcje podsystemów systemu MyTri p, wyodrębnionych w ramach jego wstępnej de-
kompozycji

Podsystem Funkcja spełniana w systemie


PlanningSubsystem Podsystem odpowiedzialny za skonstruowanie trasy (Tri p) łączącej
zadany zbiór lokalizacji ( L o c a t i on), a także za aktualizację trasy
inicjowaną przez podsystem Routi ngSubsystera.
RoutingSubsystem Podsystem odpowiedzialny za pobranie trasy (Tri p) z serwera
systemu (PI anni ngServi ce) i realizację tej trasy poprzez wskazywanie
kierowcy właściwych kierunków jazdy (Di r e c t i on) na poszczególnych
skrzyżowaniach (Crossing), stosownie do ich usytuowania względem
celu podróży ( D e s t i n a t i o n ) .

Inną heurystyką pomocną w dekomponowaniu systemu jest grupowanie obiektów po-


wiązanych funkcjonalnie. Bezpośrednie zastosowanie tej reguły, polegające na łączeniu
w osobne podsystemy obiektów uczestniczących w poszczególnych przypadkach użycia, jest
dobrym punktem wyjścia dla dekompozycji. Obiekty współdzielone między przypadki użycia
— jak obiekty Crossi ng, Desti nati on, Di r e c t i on, PI anni ngServi ce, Segment i Tri p systemu
MyTri p — odpowiedzialne za komunikowanie się ze sobą potencjalnych podsystemów, można
bądź to wydzielić w postaci osobnego podsystemu, bądź też przyporządkować je do podsys-
temu odpowiadającemu temu przypadkowi użycia, w którym obiekty te są tworzone.

Heurystyki pomocne w grupowaniu obiektów w podsystemy


• Przyporządkowuj do tego samego podsystemu obiekty uczestniczące w tym samym
przypadku użycia
• Wyodrębniaj w postaci oddzielnych podsystemów obiekty wykorzystywane do przesyłania
danych między podsystemami.
• Minimalizuj liczbę skojarzeń przecinających granice podsystemów.
• Wszystkie obiekty danego podsystemu powinny być funkcjonalnie powiązane.

Hermetyzacja podsystemów za pomocą wzorca projektowego Fasada

Dekompozycja systemu zmniejsza ogólną złożoność dziedziny realizacyjnej poprzez


redukowanie sprzężenia między podsystemami. Wzorzec projektowy Fasada (patrz sekcja A.6
i książka E. Gammy, R. Heima, R. Johnsona i J. Vlissidesa [Gamma i in., 1994])" umożliwia
dalszą redukcję zależności między klasami dzięki zamknięciu podsystemu w ramy prostego,
zunifikowanego interfejsu. Widoczna na rysunku 6.28 klasa Compiler stanowi fasadę skry-
wającą klasy CodeGenerator, Optimi zer, ParseNode, Parser i Lexer. Fasada zapewnia dostęp
jedynie do usług publicznie oferowanych przez podsystem, skrywając wszelkie inne szczegóły
obiektów tego podsystemu, co w efekcie redukuje sprzężenie między systemami.

11
Oraz cytowana już książka Head First Design Patterns. Edycja polska (Rusz głowę!), wyd. Helion, 2005
— przyp. tłum.
296 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

Rysunek 6.28. Przykład zastosowania wzorca projektowego Fasada do ukrywania prywatnych szcze-
gółów podsystemu

Podsystemy powstające w wyniku wstępnej dekompozycji często okazują się grupami


funkcjonalnie powiązanych klas, jako takie stanowią więc znakomite kandydatury na herme-
tyzację w formie pojedynczych klas pełniących funkcje fasady.

6.5. Literatura uzupełniająca


Koncepcja architektury oprogramowania po raz pierwszy pojawiła się w pracach E. W. Dijkstry
i D. Parnasa, którzy argumentowali, że struktura fragmentu oprogramowania ma znaczenie
nie mniej krytyczne niż jego zdolność do produkowania poprawnych wyników. Dijkstra jest au-
torem koncepcji architektury warstwowej i opisuje jej zastosowanie do przykładowego systemu
T.H.E. w swojej książce [Dijkstra, 1968]. Parnas w swej pracy [Parnas, 1972] opisuje nato-
miast koncepcję ukrywania informacji i dyskutuje kryteria uwzględniane przy dekompozycji
systemu.
Pojęcia sprzężenia i spoistości jako metryk związanych z architekturą oprogramowania
omówione są w pracy E. Yourdona i L. L. Constantina [Yourdon i Constantine, 1979].
Mimo iż nikt nie kwestionuje zalet architektury oprogramowania i jej znaczenia w inży-
nierii oprogramowania, literatura o tej tematyce wciąż, od dziesięcioleci, zdaje się ewoluować
w różnych kierunkach. Prawdopodobną tego przyczyną jest brak jednolitego języka do opisy-
wania różnych architektur, jak również brak analitycznych metod porównywania ich ze sobą oraz
weryfikowania ich adekwatności do specyfikacji wymagań. Prace M. Shawa i D. Garlana [Shaw
i Garlan, 1996] oraz F. Buschmanna, R. Meuniera, H. Rohnerta, P. Sommerlanda i M. Stała
[Buschmann i in., 1996] wydają się być pierwszą udaną próbą uporządkowania tego stanu
rzeczy w formie katalogu najczęściej używanych architektur. Shaw i Garlan wprowadzają po-
jęcie stylu architektonicznego, Buschmann ze współautorami prezentują natomiast wykorzy-
stywanie wzorców architektonicznych jako języka opisowego.
5.8. Ćwiczenia 297

Wiele analiz przypadku, jakie pojawiły się w ostatniej dekadzie ubiegłego wieku, podaje
ewidentne przykłady korzyści wynikających ze stosowania wzorcowych architektur oprogra-
mowania, nie tylko z technicznego punktu widzenia (czyli decyzji projektowych dotyczących
struktur i wielokrotnego wykorzystywania komponentów), lecz także z perspektywy zarzą-
dzania projektami (powtarzalnych metod organizowania zespołów i ich komunikowania się
ze sobą). W Instytucie Inżynierii Oprogramowania uniwersytetu Carnegie Mellon zgroma-
dzono i opracowano wiele materiałów na temat architektury oprogramowania i jej konkretnych
zastosowań. Prace L. Bassa, P. Clementsa i R. Kazmana [Bass i in., 2003] oraz P. Clementsa, R.
Kazmana i M. Kleina [Clements i in., 2002] zawierają opis metod i analizy przypadków zwią-
zanych z wyborem i oceną konkretnej architektury oprogramowania. Przykłady praktycznych
zastosowań architektury oprogramowania w rozwiązaniach przemysłowych znajdą natomiast
czytelnicy w książce C. Hofmeister, R. Norda i D. Soniego [Hofmeister, 2000],

6.6. Ćwiczenia
6.1. Dekomponowanie systemu na podsystemy z jednej strony zmniejsza złożoność,
z którą muszą borykać się programiści, poprzez redukowanie sprzężenia między
podsystemami i zwiększanie spoistości każdego z nich; z drugiej jednak strony przy-
czynia się do wzrostu złożoności, poprzez zwiększanie rozdrobienia systemu i liczby
interfejsów. Jeśli maksymalna spoistość ma być przesłanką przemawiającą za dziele-
niem podsystemów na coraz mniejsze części, to jaka konkurencyjna przesłanka
przemawia za utrzymywaniem liczby podsystemów na możliwie niskim poziomie?
6.2. W sekcji 6.4.2 zaklasyfikowaliśmy cele projektowe w pięciu kategoriach: wydajności,
pewności działania, kosztów, pielęgnowalności i kryteriów użytkownika. Przyporządkuj
każdy z poniższych celów do jednej lub kilku z wymienionych kategorii:
• użytkownik musi otrzymać od systemu odpowiedź najdalej po sekundzie od wy-
słania żądania,
• Ti cketDi s t r i butor musi być zdolny do wydrukowania biletu, nawet w przypad-
ku awarii sieci,
• sprzęt hostujący system Ti cketDi s t r i butor musi umożliwiać instalowanie nowych
przycisków, jeżeli wymagać tego będzie większe zróżnicowanie typów biletów,
• bankomat musi powstrzymywać ataki polegające na próbie odgadnięcia numeru
PIN za pomocą systematycznych prób.
6.3. Opracowując system, przechowujący swe dane w uniksowym systemie plików, prze-
widujesz, że w przyszłości tworzona będzie wersja Twojego systemu dla środowiska
przechowującego pliki w odmienny sposób. Zaprojektuj dekompozycję Twojego
systemu uwzględniającą tę perspektywę.
6.4. Starsze kompilatory projektowane były zgodnie z architekturą filtr-potok, w której
każdy z filtrów przekształcał otrzymane dane do pewnej postaci pośredniej i przeka-
zywał je następnemu filtrowi. Współczesne środowiska programistyczne, obejmujące
zintegrowany kompilator, edytor świadomy składni języka i debugger na poziomie
kodu źródłowego wykorzystują architekturę repozytoryjną. Spróbuj określić cele
projektowe, które prawdopodobnie stały się przyczyną tej zmiany.
298 Rozdział 6. • Projektowanie systemu — dekompozycja na podsystemy

6.5. Dla przykładu architektury model-widok-kontroler (MVC) z rysunków 6.16 i 6.17:


a) narysuj diagram sekwencji odpowiadający diagramowi komunikacyjnemu
z rysunku 6.17,
b) zastanów się, w jaki sposób architektura MVC ułatwia lub utrudnia realizowanie
następujących celów projektowych:
• rozszerzalności (polegającej na dodawaniu nowych typów widoków);
• reaktywności (objawiającej się małym opóźnieniem między wprowadzeniem
zmiany w modelu a uwzględnieniem jej na widoku);
• modyfikowalności (czyli definiowania nowych atrybutów plików i katalogów);
• kontrolowalności dostępu (czyli pewności, że tylko upoważnieni użytkownicy
będą mieć dostęp do poszczególnych części modelu).
6.6. Wymień przykładowe cele projektowe, które mogą być trudne do zrealizowania
w ramach zamkniętej architektury warstwowej w rodzaju modelu odniesienia
OSI-ISO, widocznego na rysunku 6.10.
6.7. W ramach wielu architektur — między innymi architektury trój- i czterowarstwowej
(rysunki 6.22 i 6.23) — przechowywaniem obiektów trwałych zarządza dedykowana
warstwa. Jak myślisz, które cele projektowe spowodowały podjęcie takiej właśnie
decyzji?

Bibliografia
L. Bass, P. Clements, R. Kazman Software Architecture in Practice,
[Bass i in., 2003]
wyd. drugie, Addison-Wesley, Reading, MA, 2003.

F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, M. Stal


[Buschmann i in., 1996]
Pattern-Oriented Software Architecture, John Wiley & Sons,
Chichester, 1996.

[Clements i in., 2002] P. Clements, R. Kazman, M. Klein Evaluating Software Architectures:


Methods and Case Studies, SEI Series in Software Engineering,
Addison-Wesley, 2002.
[Day i Z i m m e r m a n n , 1983] J. D. Day, H. Z i m m e r m a n n „The OSI Reference Model",
Proceedings of the IEEE, t. 71, str. 1334 - 1340, grudzień 1983.
[Dijkstra, 1968] E. W. Dijkstra The Structure of the „T.H.E" Multiprogramming
System, „Communication of the ACM" 18 (8), str. 453 - 457, 1968.
[Erman i in., 1980] L. D. Erman, F. Hayes-Roth i in. The Hearsay-IISpeech-Understanding
System: Integrating knowledge to resolve uncertainty, „ACM Computing
Surveys", t. 12, nr 2, str. 213 - 253,1980.

E. G a m m a , R. H e l m , R. Johnson, J. Vlissides Design Patterns:


[Gamma i in., 1994]
Elements of Reusable Object-Oriented Software, Addison-Wesley,
Reading, MA, 1994. Wydanie polskie Wzorce projektowe. Elementy
oprogramowania obiektowego wielokrotnego użytku, Helion 2010.

C. Hofmeister, R. Nord, D. Soni Applied Software Architecture,


[Hofmeister, 2000]
Object Technology Series, Addison-Wesley, 2000.
Bibliografia 299

[JFC, 2009] Java Foundation Classes, JDK Documentation, Javasoft, 2009.

[OMG, 2008] Object Management Group Common Object Request Broker Architecture
(CORBA) Specification: Version 3.1, http://www.omg.org 2008.

[Parnas, 1972] D. Parnas On the Criteria to Be Used in Decomposing Systems into


Modules, „Communications of the ACM", 15 (12), str. 1053 - 1058,1972.

[Ritchie i Thompson, 1974] D. M. Ritchie, K. T h o m p s o n The Unix Time-sharing System,


„Communications of the ACM", 1.17, nr 7, str. 365 - 377, lipiec 1974.

[RMI, 2009] Java Remote Method Invocation, JDK Documentation, Javasoft, 2009.

[Rumbaugh i in., 1991] J. R u m b a u g h , M. Błaha, W . Premerlani, F. Eddy, W . Lorensen


Object-Oriented Modeling and Design, Prentice Hall, Englewood
Cliffs, NJ, 1991.

[Shaw i Garlan, 1996] M. Shaw, D. Garlan Software Architecture: Perspectives on an


Emerging Discipline, Prentice Hall, Upper Saddle River, NJ, 1996.

[Yourdon i Constantine, 1979] E. Yourdon, L. L. Constantine Structured Design, Prentice-Hall,


Englewood Cliffs, NJ, 1979.
7.1. Wstęp: przykład redundancji 302

7.2. O aktywnościach projektowania systemu ogólnie 303

7.3. Koncepcje: diagramy wdrażania UML 304

7.4. Aktywności realizacji celów projektowych 306


7.4.1. Odwzorowywanie podsystemów
w procesory i komponenty 306
7.4.2. Identyfikowanie trwałych danych i ich przechowywanie 309
7.4.3. Definiowanie założeń kontroli dostępu 312
7.4.4. Projektowanie globalnego przepływu sterowania 319
7.4.5. Identyfikowanie usług 321
7.4.6. Identyfikowanie warunków granicznych 323
7.4.7. Weryfikowanie projektu systemu 326

7.5. Zarządzanie projektowaniem systemu 328


7.5.1. Dokumentowanie projektu systemu 328
7.5.2. Przydzielanie odpowiedzialności 330
7.5.3. Komunikacja w projektowaniu systemu 331
7.5.4. Iteracje projektowania systemu 333

7.6. Analiza przypadku — system ARENA 334


7.6.1. Identyfikowanie cełów projektowych 335
7.6.2. Identyfikowanie podsystemów 336
7.6.3. Odwzorowanie podsystemów w procesory i komponenty 337
7.6.4. Identyfikowanie i przechowywanie trwałych danych 339
7.6.5. Definiowanie założeń kontroli dostępu 340
7.6.6. Projektowanie globalnego przepływu sterowania 341
7.6.7. Identyfikowanie usług 343
7.6.8. Identyfikowanie warunków granicznych 345
7.6.9. Wnioski 347

7.7. Literatura uzupełniająca 348

7.8. Ćwiczenia 348

Bibliografia 349
Projekt systemu:
realizacja celów
projektowych

Dobre, szybkie, tanie. Wybierz sobie dwa.


— stary aforyzm programistyczny

W trakcie projektowania systemu identyfikujemy cele projektowe, dokonujemy dekompo-


zycji systemu na podsystemy, a następnie doskonalimy tę dekompozycję tak długo, aż zgodna
będzie ze wszystkim i zakładanymi celami. Samą dekompozycję i koncepcję celów projekto-
wych omawialiśmy w poprzednim rozdziale, ten poświęcimy natomiast aktywnościom zmie-
rzających do zrealizowania wspomnianych celów, między innymi:

• wybieraniu komponentów do realizacji systemu, ze szczególnym uwzględnieniem


komponentów „zpółki" i komponentów tradycyjnych — za pomocą takich właśnie
komponentów poszczególne systemy realizowane są bardziej oszczędnie, należy
więc mieć ten fakt na uwadze przy wstępnej dekompozycji systemu,
• odwzorowywaniu podsystemów w komponenty sprzętowe — gdy system rozproszony
jest między kilka węzłów sprzętowych, potrzebne są zwykle dodatkowe podsystemy
wynikające z wymagań dotyczących niezawodności i wydajności,
• projektowaniu infrastruktury służącej zarządzaniu trwałymi danymi — pod pojęciem
„trwałych" danych rozumiemy te, które zachowują swe istnienie po zakończeniu dzia-
łania systemu; konkretny wybór wspomnianej infrastruktury ma ogólny wpływ na
wydajność systemu i prowadzi zwykle do pojawienia się nowych podsystemów,
• specyfikowaniu polityki kontroli dostępu — dostęp do współdzielonych obiektów
systemu musi odbywać się w sposób kontrolowany; polityka kontroli dostępu okre-
śla sposób rozproszenia obiektów między poszczególne podsystemy,
• definiowaniu globalnego przepływu sterowania — sekwencja wykonywanych operacji
decyduje o postaci interfejsu poszczególnych podsystemów,
• projektowaniu obsługi warunków granicznych — gdy zidentyfikowane zostaną wszyst-
kie podsystemy, należy określić kolejność uruchamiania i kończenia pracy poszcze-
gólnych komponentów.

Następnie opiszemy zagadnienia menedżerskie związane z projektowaniem systemu, czyli


dokumentowanie, przydzielanie odpowiedzialności i komunikowanie. Rozdział zakończymy
szczegółową dyskusją na temat rozstrzygania różnych kompromisów na przykładzie systemu
ARENA.
302 Rozdział 7. • Projekt systemu: realizacja celów projektowych

7.1. Wstęp: przykład redundancji


Redundancja w systemie komputerowym promu kosmicznego
W przeciwieństwie do poprzednich statków kosmicznych, prom kosmiczny zaprojektowany został
jako układ autonomiczny. Misje p r o m ó w kosmicznych są dłuższe niż misje Apollo czy Gemini,
liczniejsza jest też załoga. Należy też liczyć się z tym, że w danym czasie może być realizowanych
kilka misji. Prom kosmiczny musi więc być zdolny do tolerowania kilku awarii, zanim zapadnie
decyzja o przedwczesnym zakończeniu misji, co wiąże się z koniecznością redundancji (dublowania)
niektórych jego elementów, między innymi systemu komputerowego odpowiedzialnego za kiero-
wanie, nawigację i kontrolę wysokości.

Dublowanie systemu komputerowego statku kosmicznego nie jest pomysłem nowym. Rakieta Saturn,
która wyniosła statek Apollo, wykorzystywała trzy identyczne kopie systemu — każdy jego kom-
ponent występował w trzech egzemplarzach. Gdy wynik produkowany przez jeden egzemplarz da-
nego komponentu różnił się od wyniku produkowanego przez dwa pozostałe egzemplarze, uzna-
wane to było za awarię: wadliwy komponent uznany zostawał za przegłosowany, przyjmowany był
wynik produkowany przez pozostałe egzemplarze — tak oto maskowana była pojedyncza awaria.
Takie rozwiązanie było po pierwsze trudne do zrealizowania, po drugie zdolne było sobie radzić
z incydentalnymi awariami, nie z masową katastrofą, jaką było na przykład spłonięcie Apollo 13.

W stacji orbitalnej Skylab zastosowano inne rozwiązanie. Dwa bliźniacze egzemplarze systemu kom-
puterowego umieszczone były na dwóch różnych krańcach statku. Gdyby zawiódł jeden z tych sys-
temów, drugi powinien przejąć jego funkcje. Podczas gdy stosunkowo długi czas przełączenia
między systemami jest całkowicie akceptowalny dla stacji orbitalnej (która do pewnego stopnia może
tracić wysokość bez kłopotliwych konsekwencji), jest on nie do przyjęcia w przypadku promu ko-
smicznego, którego komputery reagować muszą błyskawicznie na szybko następujące po sobie
zdarzenia związane ze zboczeniem z kursu lub przebiegiem lądowania. Z tego względu system kom-
puterowy promu kosmicznego powinien być zarówno zdublowany (jak w stacji Skylab), jak i szybko
przełączalny (podobnie jak w rakiecie Saturn).

Zgodnie z początkowymi wymaganiami ze strony NASA, prom kosmiczny powinien umieć pora-
dzić sobie z dwiema awariami pod rząd, bez konieczności przerywania misji: prowadziło to do sys-
temu zawierającego pięć identycznych komputerów z pięcioma uruchomionymi egzemplarzami tego
samego oprogramowania. Gdyby zawiodły dwa spośród wspomnianych komputerów, trzy pozostałe
powinny zapewnić kontynuowanie misji; gdyby zawiódł kolejny, dwa pozostałe powinny zapewnić
bezpieczne wylądowanie. Ze względu na wysokie koszty takiego rozwiązania, NASA zdecydowała
się obniżyć swe wymagania do jednej tolerowanej awarii — dwie następujące bezpośrednio po sobie
miały powodować przerwanie misji. Ponieważ jednak zaawansowany już projekt zależny był od
koncepcji pięciu komputerów (które zostały już dostarczone), piątemu komputerowi powierzono
funkcję komputera zapasowego. Zauważmy, że wszystkie cztery pozostałe komputery wykonywać
miały to samo oprogramowanie, zatem powielenie systemu nie zapewniało żadnego zabezpieczenia
przed konsekwencjami błędów programistycznych. Na wspomnianym komputerze zapasowym wy-
konywana miała być uproszczona wersja systemu, zawierająca jedynie funkcje niezbędne do prawi-
dłowej obsługi startu i lądowania.

Przedstawione przykłady stanowią doskonałą ilustrację tego, w jaki sposób podejmo-


wane są decyzje związane z wyborem architektury skomplikowanego systemu informatycznego.
Mimo iż opisywane historie mają już raczej znaczenie historyczne, większość podejmowanych
decyzji dyktowana była celami projektowymi i wymaganiami pozafunkcyjnymi. Oczywiście,
7.2. O aktywnościach projektowania systemu ogólnie 303

gdy decyzje projektowe koncentrują się głównie wokół systemu programistycznego, wymagają
nieco innego podejścia niż w przypadku promu kosmicznego, gdzie równie istotne były kwe-
stie sprzętowe; mimo to, niezmienna pozostaje zasada, że cele projektowe rozpatrywane są
pojedynczo, za każdym razem wpływając na kształt dekompozycji systemu i interfejsy po-
szczególnych podsystemów. Projektowanie systemu obejmuje przeanalizowanie kolejno
wszystkich celów projektowych. W sekcji 7.2 opisujemy ogólnie związane z tym aktywności,
w sekcji 7.3 prezentujemy nowy typ diagramów UML — diagramy wdrażania. Sekcję 7.4
poświęcamy szczegółowemu opisowi aktywności związanych z projektowaniem systemu i ich
powiązaniom. Sekcja 7.5 zawiera omówienie zagadnień menedżerskich związanych z projek-
towaniem systemu. Opisane koncepcje ilustrujemy w sekcji 7.6 w ramach analizy przypadku,
jakim jest system ARENA.

7.2. O aktywnościach projektowania systemu ogólnie


Cele projektowe stają się drogowskazem do podejmowania rozmaitych decyzji, zwłaszcza
wtedy, gdy konieczne jest rozstrzyganie rozmaitych kompromisów. Programiści dokonują
podziału systemów na prostsze podsystemy, co stanowi naturalny sposób radzenia sobie ze
złożonością: realizacja każdego podsystemu powierzona zostaje oddzielnemu zespołowi i od-
bywa się niezależnie od realizacji innych podsystemów. Aby jednak stało się to możliwe, pro-
gramiści, dokonując dekompozycji, rozstrzygać muszą problemy dotyczące systemu jako całości.
Oto one.

• Odwzorowanie oprogramowania w konkretny sprzęt. Jaka jest sprzętowa konfigu-


racja systemu? Które węzły odpowiedzialne są za poszczególne elementy funkcjo-
nalne? Jak realizowana jest komunikacja między węzłami? Które usługi realizowane
są z użyciem istniejących komponentów? Jak hermetyzowane są te komponenty?
Rozważanie tych i podobnych kwestii powoduje pojawienie się dodatkowych pod-
systemów dedykowanych przesyłaniu danych między węzłami, zarządzaniu współ-
bieżnością i zapewnianiu niezawodności. Wykorzystywanie komponentów „z pół-
ki" umożliwia bardziej ekonomiczną realizację skomplikowanych usług; pakiety
dedykowane realizacji interfejsu użytkownika oraz systemy zarządzania bazami da-
nych są najbardziej spektakularnymi przykładami takich właśnie komponentów.
Każdy z tych komponentów powinien być jednak hermetyzowany w celu wyelimi-
nowania uzależnień od niego: gdy w przyszłości konkurencyjny dostawca zaoferuje
bardziej atrakcyjny system bazy danych, zmiana systemu pod tym kątem nie będzie
wymagać znaczących ingerencji w jego kod 1 .
• Zarządzanie danymi. Które dane mają charakter trwały? W jaki sposób powinny być
przechowywane? Jak odbywać się będzie dostęp do nich? Dane trwałe okazują się
często wąskimi gardłami systemu, stanowią bowiem główny obiekt przetwarzania
przez system. Z tego powodu dostęp do trwałych danych powinien być szybki i nie-
zawodny. Jeśli odczytywanie i wyszukiwanie danych jest wolne, wolny jest cały system;
jeśli prawdopodobne jest uszkodzenie danych, równie prawdopodobne jest kompletne

1
Przykład hermetyzowania systemu bazy danych widzieliśmy już na rysunku 6.6 w poprzednim
rozdziale — przyp. tłum.
304 Rozdział 7. • Projekt systemu: realizacja celów projektowych

załamanie całego systemu. Tego typu zagadnienia muszą być rozpatrywane w sposób
spójny na poziomie całego systemu — zwykle wtedy odbywa się wybór konkretnego
komponentu bazodanowego i definiowanie towarzyszących mu podsystemów reali-
zujących zarządzanie trwałymi danymi.
• Kontrola dostępu. Kto posiada dostęp do poszczególnych części danych? Czy reguły
kontroli tego dostępu mogą być dynamicznie zmieniane? W jaki sposób reguły te są
definiowane i realizowane? Kontrola dostępu do danych i ich bezpieczeństwo także
są zagadnieniami ogólnosystemowymi — innymi słowy, polityka dostępu do okre-
ślonych partii danych musi być identyczna dla wszystkich podsystemów.
• Przepływ sterowania. Jaka jest sekwencja poszczególnych operacji w systemie? Czy
system sterowany jest zdarzeniami? Czy jest zdolny do obsługi kilku równoczesnych
interakcji? Wybór konkretnego schematu przepływu sterowania ma swe konsekwen-
cje dla interfejsów poszczególnych podsystemów: w systemie sterowanym zdarzeniami
poszczególne podsystemy muszą zapewniać obsługę tych zdarzeń, w systemie wie-
lowątkowym muszą być zdefiniowane mechanizmy szeregowania dostępu do wspól-
nych danych, w postaci wzajemnych wykluczeń i sekcji krytycznych.
• Warunki graniczne. W jaki sposób system rozpoczyna i kończy swą pracę? Jak ob-
sługiwane są sytuacje wyjątkowe? Realizacja inicjowania i kończenia pracy systemu
skupia większą część jego złożoności, szczególnie w środowisku rozproszonym. Ini-
cjowanie, kończenie pracy i obsługa sytuacji wyjątkowych mają wpływ na postać
interfejsów potencjalnie wszystkich podsystemów.

Na rysunku 7.1 widoczny jest diagram przedstawiający aktywności etapu projektowania


systemu. Każda z aktywności dedykowana jest jednemu z problemów, które wyżej wymienili-
śmy. Efektem każdej ze wspomnianych aktywności jest zwykle zmiana w dekompozycji sys-
temu, generująca kolejne problemy. W ten oto sposób projektowanie systemu staje się procesem
wysoce iteratywnym, a rezultatem każdej iteracji jest zidentyfikowanie nowych systemów
lub modyfikacja istniejących i często ogólna zmiana w sposobie dekompozycji odbijająca się
na wszystkich podsystemach, co postaramy się pokazać w tym rozdziale.

7.3. Koncepcje: diagramy wdrażania UML


Diagramy wdrażania służą w języku UML do ukazywania relacji między komponentami
wykonawczymi i węzłami infrastruktury. Komponentami są samowystarczalne encje, świad-
czące usługi na rzecz innych komponentów lub aktorów; przykładem komponentu w tym
znaczeniu jest serwer WWW, świadczący usługi na rzecz przeglądarek WWW, jak również
przeglądarka W W W (Firefox, Opera, Safari) świadcząca usługi na rzecz użytkownika. Węzeł
to fizyczne urządzenie lub środowisko wykonawcze, na którym lub w którym komponent re-
alizuje swe funkcje. System składa się ze współdziałających ze sobą komponentów czasu wy-
konywania, które mogą być rozproszone między wiele węzłów. Co więcej, węzeł jest pojęciem
rekurencyjnym — może zawierać inne węzły: przykładowo na fizycznym urządzeniu może
być zainstalowane środowisko wykonawcze. Na diagramach wdrażania węzły reprezentowane
są przez pudełka, zawierające ikony komponentów ( 3 ) . By oznaczyć fizyczne urządzenia
i środowiska wykonawcze, węzły mogą być etykietowane stereotypami. Ścieżki komunikacyjne
7.3. Koncepcje: diagramy wdrażania UML 305

Rysunek 7.1. Aktywności etapu projektowania systemu

między węzłami reprezentowane są przez linie ciągłe; linie te mogą być etykietowane stereo-
typami oznaczającymi protokoły komunikacji między parami węzłów. Na diagramie z rysunku
7.2 widoczne są dwie przeglądarki WWW, wyświetlające informację otrzymywaną z serwera
WWW; serwer ten z kolei komunikuje się z serwerem bazy danych. Zwróćmy uwagę, że żadna
z przeglądarek nigdy nie uzyskuje bezpośredniego dostępu do serwera bazy danych.

Rysunek 7.2. Diagram wdrażania reprezentujący przydział k o m p o n e n t ó w do różnych węzłów. Prze-


glądarki W W W uruchomione na pececie i macintoshu korzystają z serwera W W W (Webserver),
który z kolei komunikuje się z bazą danych (Database)
306 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Diagram wdrażania widoczny na rysunku 7.2 koncentruje się na przydziale komponen-


tów od węzłów i dostarcza wysokopoziomową informację o każdym komponencie. Informację
tę można uszczegółowić, określając interfejsy komponentów i zawarte w nich klasy. Na ry-
sunku 7.3 widoczna jest taka informacja na temat komponentu Webserver z rysunku 7.2.

Rysunek 7.3. Szczegółowa informacja na temat k o m p o n e n t u Webserver. Komponent ten dostarcza


interfejs h t t p , wymagając jednocześnie interfejsu jdbc. Interfejs h t t p realizowany jest przez klasę
HttpService

7.4. Aktywności realizacji celów projektowych


W tej sekcji opiszemy aktywności niezbędne do upewnienia się, że dekompozycja systemu
zgodna jest ze wszystkimi celami projektowymi i uwzględnia wszelkie ograniczenia fazy im-
plementacji. W sekcji 6.4 sformułowaliśmy cele projektowe systemu MyTri p i dokonaliśmy
wstępnej dekompozycji. Teraz przystąpimy do udoskonalenia tej dekompozycji poprzez:

• odwzorowywanie podsystemów w procesory i komponenty (patrz sekcja 7.4.1),


• identyfikowanie trwałych danych i ich przechowywanie (patrz sekcja 7.4.2),
• definiowanie założeń kontroli dostępu (patrz sekcja 7.4.3),
• projektowanie globalnego przepływu sterowania (patrz sekcja 7.4.4),
• identyfikowanie usług (patrz sekcja 7.4.5),
• identyfikowanie warunków granicznych (patrz sekcja 7.4.6),
• weryfikowanie projektu systemu (patrz sekcja 7.4.7).

7.4.1. Odwzorowywanie podsystemów w procesory i komponenty

Wybór platformy i konfiguracji sprzętowej

Wiele systemów funkcjonuje na więcej niż jednym komputerze i często zależy od


dostępu do internetu lub intranetu. Użycie wielu komputerów rodzi wymagania pod adresem
zarówno wydajności, jak i połączeń między wieloma rozproszonymi użytkownikami. W związku
z tym musimy starannie przeanalizować przyporządkowanie podsystemów do poszczegól-
nych komputerów i infrastrukturę realizującą komunikację między tymi podsystemami. Kom-
putery te modelowane są jako węzły na diagramach wdrażania. Ponieważ odwzorowywanie
podsystemów w węzły sprzętowe ma znaczący wpływ na złożoność i wydajność systemu, do-
konywane jest w procesie projektowania systemu możliwie jak najwcześniej. Wybór konfi-
7.4. Aktywności realizacji celów projektowych 307

guracji sprzętowej wiąże się także z wyborem docelowej maszyny wirtualnej dla systemu. Taka
maszyna wirtualna obejmuje system operacyjny i wszelkie niezbędne komponenty, takie jak
system(y) zarządzania bazą danych czy pakiety komunikacyjne. Właściwy wybór maszyny
wirtualnej redukuje dystans między systemem a platformą sprzętową, na której ma być eks-
ploatowany. Im bardziej funkcjonalne wspomniane komponenty, tym mniej pracochłonne
będzie opracowywanie samego systemu. Wybór maszyny wirtualnej może być jednak poważ-
nie ograniczony, na przykład przez fakt, że klient kontraktujący system dysponuje już odpo-
wiednią bazą sprzętową. Nie bez znaczenia są także kryteria kosztowe: być może samodzielne
zaimplementowanie jakiegoś komponentu jest bardziej opłacalne niż zakup gotowej wersji
— jednak tego typu dylematy rzadko są łatwe do rozstrzygania.
Z wymagań dotyczących systemu MyTrip wnioskujemy, że jego podsystemy Planning
^Subsystem i Routi ngSubsystem funkcjonować mają na dwóch różnych węzłach: pierwszy
na serwerze WWW realizującym odpowiednie usługi, drugi na pokładowym komputerze sa-
mochodu. Ten fakt odzwierciedlony jest na rysunku 7.4, na którym widoczne są dwa urządzenia:
:OnBoardComputer (komputer pokładowy) i :WebHost (komputer serwera WWW); na tym
ostatnim funkcjonuje środowisko wykonawcze : Apache. Jako maszynę wirtualną dla serwera
W W W wybraliśmy odmianę UNIX-a, rolę maszyn wirtualnych w komputerze pokładowym
spełniają przeglądarki Internet Explorer i Safari.

Rysunek 7.4. Przyporządkowanie podsystemów systemu MyTri p do urządzeń i środowisk wykonaw-


czych. Routi ngSubsystem funkcjonuje na komputerze pokładowym :OnBoardComputer, Planning
^•Subsystem na serwerze Apache

Przydzielanie obiektów i podsystemów do węzłów

Po zdefiniowaniu konfiguracji sprzętowej i wybraniu maszyn wirtualnych następuje


przyporządkowanie obiektów i podsystemów do poszczególnych węzłów tej konfiguracji.
Wywołuje to często konieczność definiowania nowych obiektów i podsystemów odpowie-
dzialnych za transfer danych między węzłami.
W systemie MyTrip klasy Trip, Destination, Crossing, Segment i Direction współ-
dzielone są przez oba podsystemy, czyli przez PI anni ngSubsystem i Routi ngSubsystem.
Instancje tych klas komunikują się za pomocą łączności bezprzewodowej, zgodnie z ustalo-
nym protokołem komunikacyjnym. Na potrzeby tej komunikacji definiujemy nowy podsystem
— Communi cati onSubsystem — przydzielony do obu węzłów i zarządzający wymianą danych
między nimi.
308 Rozdział 7. • Projekt systemu: realizacja celów projektowych

W podsystemie Routi ngSubsystem przechowywane są jedynie te segmenty, które skła-


dają się na zaplanowaną trasę podróży; segmenty przylegające do nich zapamiętane są je-
dynie w podsystemie PI anni ngSubsystem. W podsystemie Routi ngSubsystem potrzebne są
więc obiekty służące jako surogaty obiektów Segment i Tri p, przechowywanych w systemie
PI anni ngSubsystem. Obiekt funkcjonujący w imieniu innego obiektu określany bywa po-
wszechnie mianem proxy1. Definiujemy zatem dwie kolejne klasy — SegmentProxy i TripProxy
— jako część podsystemu Routi ngSubsystem. Klasy te są przykładem zastosowania wzorca
projektowego Proxy — patrz sekcja A.8 i książka E. Gammy, R. Heima, R. Johnsona i J. Vlis-
sidesa [Gamma i in., 1994]3.
Gdy kierowca zdecyduje się na zmianę zaplanowanej trasy, obiekty wymienionych klas
w sposób transparentny żądają od podsystemu Communi c a t i onSubsystem pobrania z pod-
systemu PI anni ngSubsystem informacji związanej z segmentami zapamiętanymi w pod-
systemie Routi ngSubsystem. W odpowiedzi podsystem Communi cati onSubsystem powoduje
przesłanie do obiektu RouteAssistant kompletnej informacji o aktualnej trasie podróży. Zre-
widowany model projektu systemu MyTri p wraz z definicjami nowych klas przedstawiamy
na rysunku 7.5 i w tabeli 7.1.

Rysunek 7.5. Zrewidowany model projektu systemu MyTri p

Generalnie przydzielanie podsystemów do węzłów konfiguracji umożliwia rozproszenie


funkcjonalności i rozdział mocy obliczeniowej stosownie do zapotrzebowania na nią. Jedno-
cześnie stwarza kolejne problemy związane z przechowywaniem, przesyłaniem, replikowa-
niem i synchronizowaniem danych pomiędzy podsystemami.

2
W języku angielskim słowo to oznacza pełnomocnika — przyp. tłum.
3
Oraz cytowana już w poprzednim rozdziale książka Head First Design Patterns. Edycja polska
(Rusz głową!), wyd. Helion, 2005 — przyp. tłum.
7.4. Aktywności realizacji celów projektowych 309

Tabela 7.1. Nowe klasy i podsystemy zrewidowanego modelu projektu systemu MyTri p

Element modelu Definicja


Communi c a t i onSubsystem Podsystem odpowiedzialny za transfer obiektów z podsystemu
PI anni ngSubsystem do podsystemu Routi ngSubsystem.
Connection Klasa reprezentująca aktywne połączenie między podsystemami
PI anni ngSubsystem i Routi ngSubsystem, odpowiedzialna także
za obsługę sytuacji wyjątkowej, jaką jest zerwanie przedmiotowego
połączenia.
Message Klasa reprezentująca komunikat niosący kompletną, zakodowaną
informację o trasie podróży i związanych z nią obiektach Tri p,
D e s t i n a t i o n , S e g m e n t , C r o s s i n g i Di r e c t i on.
SegmentProxy Klasa podsystemu Routi ngSubsystem spełniająca rolę proxy
w stosunku do klasy Segment z podsystemu PI anni ngSubsystem.

TripProxy Klasa podsystemu Routi ngSubsystem spełniająca rolę proxy


w stosunku do klasy Tri p z podsystemu PI anni ngSubsystem.

7.4.2. Identyfikowanie trwałych danych i ich przechowywanie


Danymi trwałymi nazywamy te dane, które nie giną wraz zakończeniem pracy systemu lub
programu. Przykładowo, pisząc książkę za pomocą edytora tekstu, możemy zapisać w pliku
edytowany tekst i zamknąć edytor, by po jakimś czasie otworzyć wspomniany plik ponownie,
otrzymując tekst w dokładnie takim stanie, w jakim go pozostawiliśmy. Uruchomiony edytor
nie jest warunkiem istnienia pliku przechowującego ów tekst. Podobnie ewidencja danych
pracowników i ich wynagrodzeń przechowywana jest zwykle w formie systemu zarządzania
bazą danych. Dzięki temu wszystkie programy operujące na tej informacji czynią to w sposób
spójny i skoordynowany, ponadto możliwe jest szybkie wyszukiwanie żądanej informacji
wśród wielu tysięcy rekordów.
Miejsce i sposób przechowywania danych w systemie jest jednym z najważniejszych
czynników wpływających na proces dekompozycji tego systemu: przykładowo, co widzieliśmy
w sekcji 6.3.5, w ramach architektury repozytoryjnej zarządzanie danymi może być dedyko-
wane odrębnemu podsystemowi. Wybór konkretnego systemu zarządzania bazą danych ma
także swe implikacje w kwestii ogólnej strategii przypływu sterowania i zarządzania współ-
bieżnością: i tak w podsystemie Routi ngSubsystem zdecydowaliśmy się przechowywać
aktualnie wyznaczoną trasę (Tri p) w pliku umieszczonym na wymiennym dysku, co daje kie-
rowcy możliwość zamknięcia systemu w trakcie podróży i odtworzenie trasy po jego ponownym
uruchomieniu, bez konieczności łączenia się z podsystemem PI anni ngSubsystem. To proste
rozwiązanie wystarczające jest w zupełności dla podsystemu Routi ngSubsystem jedynie za-
pamiętującego wyznaczone trasy; w podsystemie PI anni ngSubsystem, od którego wymaga się
konstruowania optymalnych tras i to dla wielu użytkowników równocześnie, poszczególne
trasy, wraz z mapami i innymi informacjami niezbędnymi do ich konstruowania, przechowy-
wane są w zaawansowanej bazie danych. W związku z tymi faktami dodaliśmy do systemu
MyTri p dwa kolejne podsystemy, co pokazujemy na rysunku 7.6 i w tabeli 7.2.
310 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Rysunek 7.6. Dekompozycja systemu MyTrip uwarunkowana konkretnym wyborem strategii prze-
chowywania danych

Tabela 7.2. Nowe elementy dekompozycji systemu MyTrip, związane z wyborem strategii przecho-
wywania danych

Element modelu Definicja


Tri pFi1eStoreSubsystem Podsystem odpowiedzialny za przechowywanie w plikach dyskowych
tras zapamiętywanych w systemie pokładowym samochodu. Ponieważ
przechowywanie tras na zewnętrznym nośniku przydatne jest głównie
w przypadku zamykania komputera pokładowego, zdecydowaliśmy
się na zapisywanie i odczytywanie jedynie k o m p l e t n y c h tras, bez
jakiejkolwiek dalszej szczegółowości.
MapDBStoreSubsystem Podsystem odpowiedzialny za przechowywanie map i tras w bazie
p o d s y s t e m u PI anni ngSubsystem. Podsystem ten zdolny jest do
równoczesnej obsługi wielu kierowców i agentów planowania.

Identyfikowanie obiektów trwałych

Gdy mowa o przechowywaniu danych, musimy najpierw zidentyfikować obiekty trwałe


podlegające temu przechowywaniu. Oczywistymi kandydatami do tej grupy wydają się być
obiekty encji zidentyfikowane na etapie analizy. W systemie MyTri p niewątpliwie przechowywane
muszą być obiekty klasy Trip i klas jej towarzyszących (Crossing, Destination, Planning
Servi ce i Segment), nie zawsze konieczne jest przechowywanie wszystkich obiektów encji
— w systemie MyTri p nie jest na przykład sensowne przechowywanie obiektów Location
i Direction, bowiem ich atrybuty wyliczane są automatycznie wraz z przemieszczaniem się
samochodu. I vice versa: obiekty encji nie wyczerpują listy obiektów trwałych: w systemie ob-
sługującym wielu użytkowników (kierowców) konieczne jest przechowywanie informacji
spersonalizowanych, prywatnych dla poszczególnych użytkowników, jak również wielu obiek-
tów brzegowych, reprezentujących między innymi pozycje okien, preferencje związane z inter-
fejsem użytkownika czy stany „długożyciowych" obiektów sterujących. Generalnie identyfiko-
wanie obiektów trwałych sprowadza się do wyszukiwania tych klas, których instancje muszą
przetrwać zakończenie pracy systemu — zarówno wskutek jego zamierzonego zamknięcia,
jak i z powodu niespodziewanej awarii. Po wznowieniu pracy system będzie mógł odtworzyć
wartości atrybutów tych instancji w sposób automatyczny lub na żądanie, gdy instancje te
okażą się potrzebne.
7.4. Aktywności realizacji celów projektowych 311

Wybór strategii przechowywania danych

Po zidentyfikowaniu obiektów trwałych podlegających przechowywaniu między ko-


lejnymi uruchomieniami systemu kolejnym istotnym zagadnieniem staje się strategia ich
przechowywania. Decyzja wyboru tej strategii jest sprawą złożoną i dyktowaną głównie przez
wymagania pozafunkcyjne. Jak szybkie ma być zapisywanie i odczytywanie obiektów? Czy
wyszukiwanie obiektów ma się odbywać w oparciu o zaawansowane zapytania? Czy obrazy
obiektów konsumują dużo pamięci i (lub) przestrzeni dyskowej? Zależnie od odpowiedzi na te
i podobne pytania, dokonywany jest wybór spośród trzech prezentowanych poniżej rozwiązań.

• Niezależne pliki. Pliki to abstrakcje magazynowania danych udostępniane przez sys-


tem operacyjny. Plik jest strukturą niskopoziomową — ciągiem bajtów opatrzonych
nazwą i prostymi atrybutami oraz elementarnymi regułami dostępu. Wszelkie pro-
blemy wynikające z wielodostępu oraz ryzyka utraty danych w przypadku awarii
muszą być rozwiązywane w ramach samej aplikacji; aplikacja ta ma jednak szero-
ki wybór technik optymalizujących zarówno rozmiary plików, jak i szybkość ich
przetwarzania.
• Relacyjna baza danych. Relacyjna baza danych jest zbiorem powiązanych tabel,
z których każda zgodna jest z pewnym predefiniowanym typem zwanym schema-
tem. Tabela może być utożsamiana z macierzą, której kolumny reprezentują po-
szczególne atrybuty danych, zaś wiersze odpowiadają poszczególnym obiektom,
określając wartości ich atrybutów w postaci krotek 4 . Odwzorowywanie skompli-
kowanych obiektów w schematy relacyjnej bazy danych jest prawdziwym wyzwa-
niem, któremu stawienie czoła ułatwiać ma kilka specjalizowanych metod, między
innymi opisywane w książce M. Błahy i W. Premerlaniego [Błaha i Premerlani, 1998],
Systemy zarządzania relacyjnymi bazami danych udostępniają metody zarządzania
współbieżnym dostępem do danych i uprawnieniami tego dostępu, dostarczają także
mechanizmy umożliwiające odzyskiwanie danych w przypadku awarii. Relacyjne
bazy danych są wykorzystywane i ulepszane od dziesięcioleci, są więc technologią
zdecydowanie dojrzałą. Choć z reguły są wysoce skalowalne i znakomicie nadają się
do zarządzania ogromnymi porcjami danych, okazują się mało efektywne dla prze-
chowywania małych porcji danych oraz danych bez określonej struktury (obrazów,
tekstów w języku naturalnym i tym podobnych).

• Obiektowa baza danych. Usługi udostępniane przez obiektowo zorientowane bazy


danych podobne są do usług oferowanych przez bazy relacyjne. W przeciwieństwie
jednak do tych ostatnich, obiektowa baza danych przechowuje dane w formie
obiektów i skojarzeń między nimi. Uwalnia to użytkownika od kłopotliwych odwzo-
rowań między obiektami aplikacji a encjami (krotkami) przechowującymi obrazy tych
obiektów, daje także programistom dodatkowe możliwości w postaci dziedziczenia
klas i abstrakcyjnych typów danych. Przyczynia się to do znacznej redukcji praco-
chłonności projektowania systemu. Obiektowe bazy danych są jednak zwykle wol-
niejsze niż bazy relacyjne, bardziej skomplikowane jest też ich strojenie.

4
Krotka (ang. tupie), zwana także „n-tką" (czyt. „entką"), to zbiór wartości przypisywanych atrybutom
z predefiniowanego zbioru — przyp. tłum.
312 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Poniżej przedstawiamy uproszczoną listę kryteriów wyboru konkretnego systemu prze-


chowywania danych. Zwróćmy uwagę, że w przypadku złożonego systemu stosować można
rozwiązania hybrydowe, na przykład niezależne pliki w połączeniu z relacyjnymi bazami da-
nych. Odwzorowywaniem złożonych obiektów w tabele baz relacyjnych i niezależne pliki zaj-
miemy się szczegółowo w rozdziale 10. „Odwzorowywanie modelu na kod".

Kryteria wyboru między niezależnymi plikami,


bazami relacyjnymi i bazami obiektowymi
Niezależne pliki — wystarczające dla danych:
• niestrukturalnych o dużej objętości (obrazy, animacje),
• tymczasowych (plik wymiany),

• o małym zagęszczeniu informacji (archiwa, dzienniki).

Bazy danych — relacyjne i obiektowe — zapewniają:


• synchronizację współbieżnego dostępu do danych,
• efektywny dostęp do drobnych szczegółów informacji,
• wielokrotne wykorzystywanie tych samych danych przez różne aplikacje, pracujące być
może na różnych platformach.

Relacyjne bazy danych najlepiej nadają się do obsługi:


• dużych zbiorów danych,
• efektywnego wyszukiwania obiektów na podstawie ich atrybutów, uwikłanych w skomplikowane
zapytania.

Obiektowe bazy danych okazują się korzystne dla:


• średniej wielkości zbiorów danych,
® wyszukiwania danych silnie bazującego na skojarzeniach między obiektami,
® obiektów powiązanych za pomocą nieregularnych skojarzeń.

7.4.3. Definiowanie założeń kontroli dostępu


W systemie obsługującym wielu użytkowników różni aktorzy posiadają zwykle odmienne
uprawnienia dostępu do poszczególnych fragmentów danych i konkretnych elementów funk-
cjonalności. „Zwykli" aktorzy mają zazwyczaj dostęp do danych, które sami tworzą, lecz już
administrator systemu ma nieograniczony dostęp do danych każdego użytkownika oraz da-
nych systemowych. Na etapie analizy wymagań rozróżnienie to modelowane jest przez przy-
porządkowywanie różnych aktorów do różnych przypadków użycia. Na etapie projektowania
systemu uprawnienia dostępu modelowane są przez odpowiednie współdzielenie obiektów
między aktorów oraz kontrolowanie reguł dostępu poszczególnych aktorów do konkretnych
obiektów. Zależnie od wymagań dotyczących zabezpieczeń i bezpieczeństwa, modelowanie to
musi uwzględniać także dodatkowe czynniki, takie jak sposób uwierzytelniania aktorów czy
techniki szyfrowania danych.
7.4. Aktywności realizacji celów projektowych 313

I tak na przykład w systemie MyTrip równoczesna obsługa wielu klientów stwarza od-
powiednie wymagania pod względem bezpieczeństwa: trasy podróży (Tri p) przechowywane
w systemie mogą być udostępniane tylko tym klientom, którzy sami je stworzyli, a nieuwie-
rzytelnieni użytkownicy w ogóle nie powinni mieć dostępu do systemu. Wymaganie to, spójne
z celami projektowymi sformułowanymi w sekcji 6.4.2, ma swe odzwierciedlenie w zdefinio-
waniu klasy Driver, reprezentującej klientów (kierowców) i skojarzonej z klasą Trip. Pod-
system PlanningSubsystem odpowiedzialny jest za uwierzytelnienie użytkownika przed
przyjęciem od niego żądania dotyczącego określonych tras. Dla większego bezpieczeństwa
dane przesyłane między podsystemami PI anni ngSubsystem i Routi ngSubsystem są szyfrowane
— funkcję tę wykonuje podsystem CommunicationSubsystem. Definicję klasy Driver oraz
zmodyfikowane definicje podsystemów PI anni ngSubsystem i Communi cati onSubsystem przed-
stawione są w tabeli 7.3; nowe elementy wyróżnione są kursywą.

Tabela 7.3. Modyfikacja modelu projektu systemu uwzględniająca uwierzytelnianie użytkowników


i szyfrowanie informacji przesyłanej między podsystemami. Nowe elementy wyróżnione są kursywę

Communi c a t i onSubsystem Podsystem odpowiedzialny za transfer obiektów z p o d s y s t e m u


PI anni ngSubsystem do podsystemu Routi ngSubsystem. Podsystem
Communi c a t i onSubsystem odpowiedzialny jest za szyfrowanie
i odszyfrowanie przesyłanej informacji; klucz szyfrowania konstruowany
jest na podstawie profilu zalogowanego użytkownika (Dri ver).
PlanningSubsystem Podsystem odpowiedzialny za skonstruowanie trasy Podróży łączącej
zadany zbiór Lokal i zac j i , a także za aktualizację trasy inicjowaną
przez podsystem Routi ngSubsystem. Przed przyjęciem żądania podsystem
PI anni ngSubsystem dokonuje uwierzytelnienia kierowcy (Dri ver)
logującego się za pośrednictwem podsystemu Routi ngSubsystem.
Po pomyślnym uwierzytelnieniu kierowca (Driver) otrzymuje listę
tras (Tri pj, które sam utworzył i do pobrania których jest uprawniony.
Driver Kierowca, zalogowany użytkownik. Klasa wykorzystywana przez
podsystem Routi ngSubsystem do zapamiętywania klucza szyfrującego
skojarzonego z użytkownikiem, zaś przez podsystem PI anni ngSubsystem
do reprezentowania skojarzeń między zarejestrowanymi użytkownikami
a należącymi do nich trasami (Trip).

Definiowanie kontroli dostępu dla typowego systemu jest zwykle bardziej skompliko-
wane niż w systemie MyTri p, dla każdego aktora konieczne jest bowiem precyzyjne określenie,
jakie operacje ma prawo wykonywać na poszczególnych obiektach. Przykładowo kasjer w banku
ma prawo realizować uznania i obciążenia na koncie klienta, ale tylko do pewnego predefi-
niowanego limitu kwotowego; transakcja wykraczająca poza ten limit musi być zatwierdzona
przez menedżera. Podobnie każdy z menedżerów może analizować statystyki ze swego oddziału,
nie ma jednak dostępu do statystyk z innych oddziałów. Analityk ma dostęp do wszystkich
statystyk, nie może jednak modyfikować żadnego z kont. Reguły dostępu poszczególnych ak-
torów do poszczególnych obiektów najłatwiej modelować za pomocą macierzy, której wiersze
odpowiadają aktorom, a kolumny — klasom podlegającym kontroli. Każda komórka tej ma-
cierzy, odpowiadająca parze (aktor-klasa) zawiera listę operacji (zwaną prawami dostępu),
jakie dany aktor ma prawo wykonywać na instancjach danej klasy. Tabela 7.4 jest przykładem
takiej właśnie macierzy.
314 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Tabela 7.4. Macierz dostępu w systemie bankowym. Kasjer m a prawo dokonywania drobnych trans-
akcji (postSmallDebit() i p o s t S m a l l C r e d i t ( ) ) i przeglądania stanów kont (examineBalance()). Me-
nedżer ma prawo dokonywania wszelkich transakcji na kontach (postSmal 1 Debi t (), postSmal 1 Credi t (),
p o s t L a r g e D e b i t ( ) i p o s t L a r g e C r e d i t Q ) , przeglądania stanów i historii kont (examineBalanceQ
i examineHistory()) i analizowania statystyk dotyczących własnego oddziału (examineBranchStats()).
Analitycy mają dostęp do wszystkich statystyk (examineGlobalStatsQ i examineBranchStatsQ), nie
mogą jednak wykonywać żadnych transakcji na kontach

Aktor/Klasa Corporation (bank) Local Branch (oddział) Account (konto)


Teł 1 er (kasjer) postSmallDebitO
postSmallCredit()
examineBalanceQ
Manager (menedżer) examineBranchStatsQ postSmallDebitO
postSmallCredit()
postLargeDebit()
postLargeCreditQ
examineBalance()
examineHistory()
Analyst (analityk) xamineGlobalStats() examineBranchStats()

Macierz kontroli dostępu jest pewnym abstrakcyjnym elementem modelu, wymagającym


konkretnej fizycznej reprezentacji w systemie. Spośród wielu możliwych reprezentacji naj-
częściej wykorzystywane są trzy. Oto one.

• Globalna tabela dostępu, w której każda niepusta komórka macierzy dostępu re-
prezentowana jest w postaci jednej lub kilku krotek postaci (aktor, klasa, operacja).
Rozstrzyganie, czy określony aktor ma prawo wykonywać określoną operację na
obiektach określonej kl asy, sprowadza się do poszukiwania odpowiedniej krotki
— gdy taka nie istnieje, aktor nie uzyska zezwolenia.
• Listy kontroli dostępu, po jednej liście dla każdej klasy; każda z tych list składa się
z par postaci (aktor, operacja). Przy każdej próbie wykonania przez określonego
aktora określonej operacji na obiekcie danej klasy, w liście związanej z tą klasą
poszukiwana jest odpowiednia para — gdy nie zostanie znaleziona, aktor nie uzyska
zezwolenia. Przykładem takiej listy jest lista gości zaproszonych na imprezę: każdy
przybyły gość konfrontowany jest przez organizatora z listą zaproszonych osób, na
której obecność jest warunkiem koniecznym i wystarczającym do udziału w imprezie.
• listy uprawnień, po jednej liście dla każdego aktora; każda z tych list składa się z par
postaci (ki asa, operacja). Przy każdej próbie wykonania przez określonego aktora
określonej operacji na obiekcie danej klasy, w liście związanej z tym aktorem po-
szukiwana jest odpowiednia para — gdy nie zostanie znaleziona, aktor nie uzyska
zezwolenia. Analogią takiej listy może być zbiór zaproszeń, jakimi aktualnie dys-
ponuje bywalec różnych imprez — w danej imprezie może uczestniczyć wyłącznie
na podstawie odpowiedniego zaproszenia..

Wszystkie z wymienione reprezentacje niosą ze sobą identyczną informację, lecz różnią


się między sobą pod względem konsekwencji dla wydajności systemu. Globalne tabele dostępu
7.4. Aktywności realizacji celów projektowych 315

cechują się dużym zapotrzebowaniem na pamięć; listy kontroli dostępu są bardziej efektywne
przy rozstrzyganiu uprawnień dostępu z perspektywy konkretnej klasy, zaś listy uprawnień
— bardziej efektywne przy rozstrzyganiu uprawnień dostępu z perspektywy konkretnego aktora.
Każdy wiersz macierzy dostępu reprezentuje konkretny „widok dostępowy" do instancji
klas ze zbioru określonego przez zbiór kolumn tej macierzy. Owe „widoki" powinny być spójne
w obrębie całej macierzy, mimo to, są one zwykle implementowane poprzez definiowanie
osobnych subklas dla różnych typów krotek (aktor, operacja). Przykładowo w omawianym
przed chwilą systemie bankowości moglibyśmy w tym celu zaimplementować dwie subklasy
klasy Account — AccountVi ewedByTel 1 er i AccountVi ewedByManager, reprezentujące (odpo-
wiednio) konto w postaci dostępnej dla kasjera (Tel 1 er) i konto w postaci dostępnej dla me-
nedżera (Manager). Tylko wybrane klasy będą wówczas dostępne dla określonego aktora
— i tak na przykład nie istnieje podklasa klasy Account odpowiednia dla analityka (Analyst),
ten bowiem nie ma prawa wykonywać żadnych operacji na kontach. W oprogramowaniu
przeznaczonym dla analityka nie będzie więc zaimplementowana żadna klasa, która takie
operacje mogłaby umożliwiać — zmniejsza to ryzyko uzyskania przez niego nieautoryzowanego
dostępu do kont w rezultacie ewentualnych błędów w oprogramowaniu.
Często liczba aktorów i (lub) liczba chronionych klas jest na tyle duża, że imple-
mentacja uprawnień w postaci zarówno list kontroli dostępu, jak i list uprawnień stano-
wiłaby zbyt duże obciążenie pamięciowe. W takich przypadkach implementuje się macierz
dostępu w postaci bardziej zwartej — w formie zbioru reguł. Przykładem takiej reprezentacji
są zapory sieciowe {firewalls) chroniące usługi zlokalizowane w intranecie przed dostępem
ze strony komputerów podłączonych do internetu. Bazując na adresie hosta źródłowego, nu-
merze portu źródłowego, adresie hosta docelowego, numerze portu docelowego i rozmiarze
pakietu zapora sieciowa decyduje o dopuszczeniu albo niedopuszczeniu tego pakietu do kom-
putera docelowego. Ponieważ liczba potencjalnych kombinacji opisanej postaci jest praktycz-
nie nieskończona, są one uogólniane w postaci ciągu3 reguł: dla każdego pakietu docierające-
go do zapory analizowane są kolejne reguły w celu znalezienia reguły „pasującej" do tegoż
pakietu; gdy taka zostanie znaleziona, stosowana jest akcja określona przez tę regułę (czyli za-
akceptowanie albo odrzucenie pakietu). Dla kompletności, ostatnia reguła na liście powinna
pasować do dowolnego pakietu — będzie on wówczas określać akcję domyślną dla pakietów,
dla których nie stosuje się żadna z pozostałych reguł. W tabeli 7.5 widzimy ciąg reguł dla
zapory widocznej na rysunku 7.7. Dwie pierwsze reguły zezwalają dowolnemu hostowi
(zlokalizowanemu w internecie lub w intranecie) na dostęp do usług http oferowanych przez
serwer W W W oraz na dostarczanie poczty do serwera pocztowego (protokół smtp). Dwie
kolejne reguły pozwalają dowolnemu komputerowi znajdującemu się w intranecie na mody-
fikowanie stron zapamiętanych na serwerze WWW (rsync) i odczytywanie poczty z serwera
pocztowego (pop). Ze względy na owe dwie reguły, żaden z hostów zlokalizowanych w inter-
necie nie ma prawa modyfikowania treści w ramach serwera W W W ani odczytywania poczty
z serwera pocztowego — i w ogóle korzystania z usług obu tych serwerów (oczywiście, z wy-
jątkiem usług będących przedmiotem dwóch pierwszych reguł). Dla czytelności i dla celów
niezawodności fakt ten został wyartykułowany w postaci trzech następnych reguł blokujących
pakiety (wiersze 5 - 7 w tabeli 7.3).

3
„Ciągu", a nie „zbiom", ponieważ istotna jest kolejność stosowania poszczególnych reguł — przyp. tłum.
316 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Rysunek 7.7. Zapora sieciowa filtrująca pakiety: filtr zlokalizowany w routerze decyduje o akcepto-
waniu lub odrzucaniu poszczególnych pakietów na podstawie zawartej w nich informacji

Tabela 7.5. Uproszczony przykład ciągu reguł filtrowania pakietów dla zapory widocznej na rysunku 7.7

Host źródłowy Host docelowy Port docelowy Akcja


dowolny 6
serwer W W W http zezwól
dowolny serwer W W W smtp zezwól
host w intranecie serwer W W W rsync zezwól
host w intranecie serwer pocztowy pop zezwól
host w internecie serwer W W W rsync zablokuj
host w internecie serwer pocztowy pop zablokuj
host w internecie host w intranecie dowolny zablokuj
dowolny dowolny dowolny zablokuj

Gdy liczba aktorów jest duża, ciąg reguł filtrujących jest znacznie bardziej zwarty od list
kontroli dostępu i list uprawnień; co więcej, niewielki stosunkowo ciąg reguł jest bardziej
czytelny i łatwiejszy do zrozumienia (oraz udowodnienia poprawności) przez człowieka, co
w przypadku systemu o wysokich wymaganiach w kwestii bezpieczeństwa jest czynnikiem
krytycznym.
Niezależnie od konkretnej implementacji, macierz dostępu reprezentuje statyczną po-
litykę dostępu. Oznacza to, że reguły dostępu mogą być modelowane w postaci atrybutów
obiektów systemu. W naszym przykładowym systemie bankowym przyjrzyjmy się brokerowi,
któremu przydzielono zbiór portfolio. Z założenia żaden broker nie ma dostępu do portfolio
zarządzanych przez innych brokerów. W tej sytuacji uprawnienia dostępu brokerów do po-
szczególnych portfolio mają charakter dynamiczny i jako takie muszą być modelowane. Na

6
„Dowolny" oznacza jakikolwiek komputer w internecie lub intranecie, również wspomniany ser-
wer W W W i serwer pocztowy.
7.4. Aktywności realizacji celów projektowych 317

rysunku 7.8 widzimy przykład realizacji' takiego modelu przy użyciu wzorca projektowego
Proxy (patrz sekcja A.8 i wspominana już książka E. Gammy i innych [Gamma i in., 1994]).
Dla każdego portfolio definiujemy mianowicie chroniący je przed nieuprawnionym dostępem
obiekt Portfol ioProxy. Skojarzenie Access między uprawnionym brokerem a obiektem
Portfol i oProxy reprezentuje próbę dostępu brokera do tegoż obiektu. Broker w celu uzyska-
nia dostępu do odpowiedniego portfolio wysyła odpowiedni komunikat do proxy chroniącego
to portfolio; proxy sprawdza najpierw istnienie skojarzenia ze wspomnianym brokerem i jeśli
skojarzenie takie istnieje, komunikat delegowany jest do odpowiedniego portfolio (przy braku
skojarzenia komunikat jest blokowany i próba dostępu kończy się niepowodzeniem).

Rysunek 7.8. Dynamiczna kontrola dostępu implementowana za pomocą ochronnego proxy. Klasa
skojarzeniowa Access zawiera zbiór operacji wykorzystywanych przez brokera do uzyskania dostępu do
konkretnego portfolio. Każda z operacji wywołuje operację i sAccessi bl e ( ) w celu sprawdzenia, czy
żądanie dostępu jest legalne, i zależnie od wyniku tego sprawdzenia komunikat wysłany przez brokera
jest delegowany do docelowego obiektu Portfol i o lub blokowany. Pojedyncze skojarzenie Access może
być używane do kontrolowania dostępu do wielu portfolio (stąd skojarzenie „jeden na wiele")

W obu przypadkach — statycznej i dynamicznej kontroli dostępu — zakładamy, że


aktor jest znany, bo jest nim konkretny użytkownik przy klawiaturze komputera lub konkretny
podsystem. Proces weryfikacji istnienia skojarzenia między tożsamością użytkownika a sys-
temem nosi nazwę uwierzytelniania. Powszechnie używany mechanizm uwierzytelniania
opiera się na podaniu (ogólnie znanej) nazwy użytkownika oraz hasła (znanego tylko użyt-
kownikowi i systemowi, przechowującemu je w listach kontroli dostępu). Zapamiętane w sys-
temie hasła użytkowników zapamiętywane są w postaci zaszyfrowanej i w takiej też postaci
konfrontowane są z nimi hasła podawane przez użytkowników przy logowaniu. Jeśli nikt
z pozostałych użytkowników nie zna hasła użytkownika logującego się, możemy być pewni
autentyczności tego ostatniego. Mimo iż uwierzytelnianie za pomocą haseł można uczynić
bezpiecznym przy użyciu współczesnych technologii, jest ono obarczone kilkoma oczywi-
stymi mankamentami. Jeżeli hasło łatwe jest do zapamiętania, może być też łatwe do odgad-
nięcia przez intruza; hasła skomplikowane, trudne do zapamiętania (i odgadnięcia) użyt-
kownicy zwykli zapisywać i przechowywać w pobliżu komputera (na przykład na kartkach
przyczepionych do monitora), co sprawia, że stają się łatwe do przechwycenia przez każdego
chętnego. Na szczęście, istnieje wiele innych, bardziej niezawodnych mechanizmów uwierzy-
telniania, na przykład połączenie tradycyjnych haseł z kartami inteligentnymi — nie wystarczy
318 Rozdział 7. • Projekt systemu: realizacja celów projektowych

znać hasła, trzeba jeszcze posiadać odpowiednią kartę. Kolejnymi zabezpieczeniami mogą
być detektory cech biometrycznych: czytniki linii papilarnych czy analizatory gałek ocznych
— utożsamienie się pod względem tych wzorców z legalnym użytkownikiem jest dla intruza
znacznie trudniejsze niż kradzież karty chipowej.
W środowisku, w którym zasoby współdzielone są przez wielu użytkowników, uwie-
rzytelnianie nie zawsze okazuje się wystarczającym zabezpieczeniem. W środowisku siecio-
wym na przykład łatwo intruzowi podsłuchiwać (za pomocą odpowiednich narzędzi) pakiety
generowane przez innych użytkowników (patrz rysunek 7.9). Tak się niedobrze składa, że po-
wszechnie dziś używane protokoły sieciowe, takie jak TCP/IP, nie był)' projektowane ze szcze-
gólną troską o bezpieczeństwo: intruz może fabrykować pakietv tak, by wyglądały jak gene-
rowane przez legalnych użytkowników.

Rysunek 7.9. Atak bierny: wykorzystując obecną technologię, pasywny intruz może podsłuchiwać
cały ruch sieciowy. Aby utrudnić m u działania destruktywne, szyfruje się przesyłaną informację, co
sprawia, że staje się trudna do zrozumienia

Żeby utrudnić (uniemożliwić?) intruzowi niecne wykorzystywanie przechwytywanych


pakietów, stosuje się szyfrowanie. Algorytm szyfrowania przekształca komunikat jawny („tekst
otwarty") w postać zaszyfrowaną („szyfrogram"), która bezużyteczna jest dla intruza nieznają-
cego klucza wykorzystywanego przez ów algorytm (klucz ten jest — oczywiście — znany ad-
resatowi, który odtwarza pierwotną wersję komunikatu). Gdyby intruzowi udało się poznać
ów klucz, można go w każdej chwili łatwo zmienić.
Bezpieczne uwierzytelnianie i szyfrowanie jest ogólnie trudnym problemem, z którym
skutecznie można sobie poradzić, wykorzystując gotowe algorytmy i pakiety zamiast projek-
towania własnych (chyba że sam tworzony system jest takim właśnie pakietem). Wiele z tych
pakietów bazuje na publicznie znanych standardach, zweryfikowanych zarówno w środowi-
skach akademickich, jak i w zastosowaniach przemysłowych, zapewniających zatem relatywnie
wysoki poziom niezawodności i bezpieczeństwa.
Wykorzystywanie gotowych i sprawdzonych rozwiązań upraszcza implementowanie
specyficznej dla aplikacji polityki bezpieczeństwa, niemniej jednak opracowanie tej polityki
i tak jest dość trudnym zadaniem. Pomocnymi w jego rozwiązywaniu mogą okazać się przy-
padła testowe i scenariusze uwzględniające przynajmniej najbardziej typowe przejawy hakerskiej
inwencji intruza. Powrócimy do tej kwestii w następnym rozdziale, przy okazji omawiania
systematycznego modelowania podobnych problemów.
7.4. Aktywności realizacji celów projektowych 319

7.4.4. Projektowanie globalnego przepływu sterowania


Pod pojęciem przepływu sterowania rozumiemy określoną sekwencję akcji wykonywanych
przez system. W systemie obiektowo zorientowanym projektowanie przepływu sterowania
polega na określeniu zbioru i kolejności wykonywanych akcji. Decyzje w tym względzie ba-
zują na zdarzeniach zewnętrznych inicjowanych przez aktorów albo na upływie czasu. Przepływ
sterowania jest zagadnieniem charakterystycznym dla etapu projektowania; nie istnieje ono
na etapie analizy, kiedy to milcząco zakłada się, że każdy obiekt może wykonywać swe operacje
w dowolnym czasie, niezależnie od pozostałych obiektów; na etapie projektowania systemu
musimy wziąć jednak pod uwagę oczywistą prawdę, że nie każdy obiekt może pozwolić sobie
na luksus posiadania własnego procesora. Istnieją trzy główne schematy organizacji przepływu
sterowania w systemie. Oto one.
• Sterowanie proceduralne. Gdy procedury realizujące operacje potrzebują danych
od aktora, zawieszają swe wykonywanie, oczekując na wprowadzenie tych danych.
Schemat ten charakterystyczny jest dla większości tradycyjnych systemów, szczególnie
stworzonych przy użyciu języków proceduralnych. W przypadku języków obiektowo
zorientowanych staje się on istotnym problemem: analizując sekwencje operacji
rozproszone między dużą liczbę obiektów, trudno określić sekwencję żądania po-
szczególnych porcji danych. Przykładowy kod prezentujący sterowanie proceduralne
przedstawiamy na listingu 7.1.

Listing 7.1. Przykład sterowania proceduralnego (w języku Java): widoczny kod wypisuje kolejne
komunikaty i po każdym z nich oczekuje na wprowadzenie danych

Stream in, out;


String userid, passwd;
/* pomijamy czynności inicjacyjne */
out.print1n("Użytkowni k:");
in.readln(userid);
out.println("Has?o:");
in.read!n(passwd);
i f (!security.check(userid, passw)) {
out.println("Nieprawid?owa nazwa użytkownika lub hasło.");
system.exit(-l);
}
/ * •••*/

• Sterowanie zdarzeniowe. W ramach tego schematu (patrz listing 7.2) w głównej pętli
powtarzany jest cykl obejmujący oczekiwanie na (dowolne) zdarzenie i po jego
wystąpieniu przekazanie go do obsługi przez odpowiednie obiekty, określone na pod-
stawie zawartości komunikatu informującego o wspomnianym wystąpieniu. Zaletą
takiego rozwiązania jest scentralizowanie wprowadzania informacji wejściowej
w pojedynczej, głównej pętli, co prowadzi do prostszej struktury programu. Bardziej
skomplikowane staje się natomiast implementowanie wielokrokowych sekwencji.
320 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Listing 7.2. Przykład sterowania zdarzeniowego (w języku Java). W każdym obrocie głównej pętli ze
strumienia eventStream pobierany jest komunikat związany z kolejnym zdarzeniem i przekazywany
do obiektów zainteresowanych wystąpieniem tego zdarzenia

Iterator subscribers, eventStream;


Subscriber subscriber;
Event event;
EventStream eventStream;
/* ... */
w h i l e (eventStream.hasNext()) {
event = eventStream.next();
subscribers = dispatchlnfo.getSubscribers(event);
w h i l e (subscribers.hasNext()) {
subscriber = subscribers.next()) {
subscriber.process(event);
}
}
/* - V

• Sterowanie wielowątkowe. Ten schemat jest współbieżną odmianą sterowania pro-


ceduralnego. System może utworzyć dowolną liczbę wątków, z których każdy zwią-
zany będzie z określonym zdarzeniem. Gdy wątek potrzebuje określonych danych,
zatrzymuje się w oczekiwaniu na ich dostarczenie przez konkretnego aktora (patrz
listing 7.3). Schemat wielowątkowy jest najbardziej intuicyjny spośród opisywanych
tutaj, wymaga jednak specjalizowanych narzędzi do debugowania; ponadto niede-
terminizm wprowadzany przez wywłaszczanie wątków znacznie utrudnia projek-
towanie i przeprowadzanie testów.

Listing 7.3. Przykład wielowątkowej obsługi zdarzeń (w języku Java). Obiekt eventHandl er, dedykowany
obsłudze zdarzeń, posiada metodę run (), wywoływaną w rezultacie uruchomienia nowego wątku

Thread thread;
Event event;
EventHandler eventHandl er;
boolean done;
/ * •••*/
while ('.done) {
event = eventStream.getNextEvent();
eventHandler = new EventHandler(event)
thread = new Thread(eventHandler);
thread.start();
}
* j

Sterowanie proceduralne jest bardzo wygodne z punktu widzenia testowania podsyste-


mów, łatwe staje się bowiem wywoływanie poszczególnych metod oferowanych przez podsystem.
Docelowo jednak należy unikać jego stosowania.
7.4. Aktywności realizacji celów projektowych 321

Wybór między sterowaniem zdarzeńiowym a sterowaniem wielowątkowym jest już


bardziej problematyczny. Sterowanie zdarzeniowe jest mechanizmem bardziej dojrzałym niż
wielowątkowość: nowoczesne języki programowania dopiero od niedawna oferują wsparcie
dla programowania wielowątkowości, a narzędzia służące do debugowania aplikacji wielo-
wątkowych pozostawiają wciąż jeszcze wiele do życzenia. Ponadto wiele pakietów dedyko-
wanych interfejsowi użytkownika zrealizowano w technice sterowania zdarzeniami i taki
też schemat sterowania wymuszają one na całym podsystemie. Mimo iż sterowanie wielowąt-
kowe jest bardziej intuicyjne, związane z nim wciąż problemy dotyczące głównie debugowania
i testowania sprawiają, że jest ono raczej melodią przyszłości i obecnie preferowane jest stero-
wanie zdarzeniowe.
Po wyborze odpowiedniego schematu sterowania przychodzi kolej na jego realizację za
pomocą jednego lub kilku obiektów sterujących. Ich zadaniem jest rejestrowanie zdarzeń ze-
wnętrznych, tymczasowe przechowywanie informacji o ich stanie oraz inicjowanie właściwej
sekwencji operacji wykonywanych przez obiekty brzegowe i obiekty encji związane z poszcze-
gólnymi zdarzeniami. Zlokalizowanie w pojedynczym obiekcie decyzji o wyborze schematu
sterowania dla przypadku użycia nie tylko daje w wyniku bardziej zrozumiały kod, lecz także
czyni sam system bardziej elastycznym z perspektywy przyszłych zmian w implementacji prze-
pływu sterowania.

7.4.5. Identyfikowanie usług


Omówiliśmy już kluczowe decyzje wpływające na kształt dekompozycji systemu: zidentyfi-
kowaliśmy główne podsystemy i 2yskaliśmy ogólne wyobrażenie o odpowiedzialności każdego
z nich. Obecnie dokonamy kolejnego udoskonalenia wspomnianej dekompozycji, identy-
fikując usługi świadczone przez każdy z podsystemów. Rozpatrzymy każdą zależność między
podsystemami i dla każdej zidentyfikowanej usługi zdefiniujemy odpowiedni interfejs (repre-
zentowany przez „lizak" notacji UML)7. W następnym etapie — etapie projektowania obiek-
tów — określimy precyzyjnie każdą usługę w kategoriach operacji, parametrów i ograniczeń
(patrz rozdział 9. „Projektowanie obiektów: specyfikowanie interfejsów").
Skupiając się na zależnościach między podsystemami, sprecyzujemy odpowiedzialność
każdego z podsystemów, odnajdziemy przeoczenia w aktualnej postaci dekompozycji i zwe-
ryfikujemy obecną architekturę aplikacji. Skupiając się na usługach (a nie na atrybutach czy
operacjach), pozostaniemy na architektonicznym szczeblu abstrakcji, co pozwoli uniknąć
większych zmian w modelu w razie zmian w przyporządkowywaniu odpowiedzialności
poszczególnym podsystemom.
Zaczniemy od interfejsu dla podsystemu Communi c a t i onSubsystem systemu MyTri p.
Zadaniem tego podsystemu jest transmitowanie kompletnych tras z podsystemu Planning
•-•Subsystem do podsystemu Routi ngSubsystem. Transmitowanie to następuje z inicjatywy
podsystemu Routi ngSubsystem, który uruchamiany jest i zamykany selektywnie, w przeci-
wieństwie do aktywnego cały czas podsystemu PI anni ngSubsystem; asymetria ta prowadzi
do zdefiniowania trzech następujących interfejsów (patrz rysunek 7.10):

7
Patrz sekcja 6.3.2 — przyp. tłum.
322 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Rysunek 7.10. Efekt kolejnego udoskonalenia dekompozycji systemu w wyniku zidentyfikowania


usług oferowanych przez poszczególne podsystemy. Podsystem Communi c a t i onSubsystem dostarcza
trzy usługi związane z zarządzaniem połączeniem oraz przesyłaniem tras (w obu kierunkach)

• Connecti onManager — umożliwia podsystemom rejestrowanie się w podsystemie


Communi cati onSubsystem w powiązaniu z uwierzytelnianiem, odnajdywanie innych
podsystemów oraz nawiązywanie i zamykanie połączeń,
• TripRequester — udostępnia listę dostępnych tras i umożliwia pobieranie tras
wybieranych z tej listy,
• Tri pProvi der — dostarcza listę tras dostępnych dla zalogowanego użytkownika
i realizuje żądania udostępnienia wybranej trasy.

Mimo iż nie zdefiniowaliśmy jeszcze żadnej operacji podsystemu Communi c a t i on


•-•Subsystem, określenie wymienionych usług dostarcza nowe szczegóły umożliwiające zi-
dentyfikowanie przeoczonych dotąd aspektów funkcjonalnych, między innymi:

• Czy podsystem Routi ngSubsystem powinien mieć możliwość dostarczania pod-


systemowi PI anni ngSubsystem informacji statystycznych na temat realizowanych
tras (średnia prędkość poruszania się, lokalizacje częstych „korków" i tym podobne)
do przyszłego użytku?
• Czy podsystem Communi cati onSubsystem powinien przesyłać wyłącznie kom-
pletne trasy, czy też powinien zapewniać możliwość przesyłania także pojedynczych
segmentów?

Powyższe pytania prowadzą do kolejnych decyzji projektowych. Do podsystemu


Communi c a t i onSubsystem dołączamy usługę wysyłania do systemu PI anni ngSubsystem
danych zbieranych w czasie rzeczywistym.. Interfejs podsystemu Communi cati onSubsystem
jest wystarczająco elastyczny, by poprzez odpowiednią strukturalizację zapewnić dowolną
politykę pobierania tras.
7.4. Aktywności realizacji celów projektowych 323

Zwróćmy uwagę na konwencję nazewnictwa usług — wybraliśmy do tej roli rzeczowniki


(na przykład Tri pRequester) zgodne z interfejsem obejmującym zarówno atrybuty, jak i ope-
racje. Operacje opatrywane są nazwami w postaci czasowników, rozpoczynającymi się od małej
litery (na przykład r e q u e s t T r i p ( ) ) . Nazwy atrybutów są rzeczownikami i rozpoczynają się
z małej litery (na przykład connecti onStatus).
Rozpoznanie zależności między podsystemami i zidentyfikowanie odpowiadających im
usług daje wystarczające zrozumienie pracy systemu w stanie ustalonym, zajmijmy się zatem
jego stanami granicznymi, między innymi uruchamianiem i zamykaniem.

7.4.6. Identyfikowanie warunków granicznych


W poprzednich sekcjach zajmowaliśmy się projektowaniem i doskonaleniem dekompozycji
systemu. Określiliśmy kształt tej dekompozycji, rozdział przypadków użycia pomiędzy po-
szczególne podsystemy, strategię przechowywania danych oraz mechanizmy kontroli dostępu
i zabezpieczeń. Brakuje wciąż rozpoznania warunków granicznych pracy systemu, czyli spo-
sobu jego uruchamiania, inicjowania jego pracy i zamykania, a także radzenia sobie z różnymi
sytuacjami wyjątkowymi w rodzaju uszkodzenia danych czy przerwania połączenia sieciowego,
niezależnie od przyczyny, którą może być błąd w oprogramowaniu lub zwykłe wyłączenie za-
silania. Przypadki użycia odzwierciedlające takie warunki nazywać będziemy granicznymi
przypadkami użycia.
Powróćmy do systemu MyTri p: mamy już znakomity obraz jego funkcjonowania, nie
powiedzieliśmy jednak ani słowa na temat tego, jak rozpoczyna swą pracę. Mówiliśmy o ma-
pach przechowywanych w bazie usługi PI anni ngServi ce — jak znalazły się w tej bazie? Jak
system instalowany jest w komputerze pokładowym samochodu? W jaki sposób identyfikuje
usługę PI anni ngServi ce, z którą ma się połączyć? W jaki sposób nowi kierowcy rejestrują się
w jego bazie? W taki oto sposób odkrywamy kolejne braki w zestawie przypadków użycia.
Jest powszechną praktyką, że graniczne przypadki użycia nie są w ogóle rozpatrywane
na etapie analizy, a w najlepszym razie traktowane są oddzielnie od „normalnych" przypad-
ków użycia. Przykładowo wiele funkcji z zakresu administrowania systemem — definiowanie
nowych użytkowników, definiowanie reguł kontroli dostępu — można zidentyfikować na pod-
stawie codziennych wymagań użytkowników, podczas gdy wiele innych funkcji to konsekwen-
cje decyzji projektowych (rozmiary cache, lokalizacja serwera bazy danych i serwera zapa-
sowego) niemających bezpośredniego związku z wymaganiami użytkowników. Generalnie
identyfikację granicznych przypadków użycia rozpoczyna się od przeanalizowania wszyst-
kich obiektów trwałych, w poszczególnych podsystemach, pod kątem wymienionych niżej
własności.

• Konfigurowanie. Dla każdego obiektu trwałego identyfikujemy przypadki użycia,


w których jest on tworzony, niszczony lub archiwizowany. Dla obiektów, które nie
są tworzone ani niszczone w ramach „normalnych" przypadków użycia, takich jak
mapy (Map) w systemie MyTri p, definiujemy nowe przypadki użycia inicjowane przez
administratora systemu (czego przykładem może być przypadek ManageMaps w sys-
temie MyTri p).
324 Rozdział 7. • Projekt systemu: realizacja celów projektowych

o Uruchamianie i zamykanie. Dla każdego komponentu (na przykład Webserver)


definiujemy trzy przypadki użycia odzwierciedlające jego uruchamianie, zamykanie
i konfigurowanie. Jak pamiętamy, w ramach jednego przypadku użycia można mo-
delować zachowanie się całej grupy ściśle powiązanych komponentów.
• Obsługa sytuacji wyjątkowych. Dla każdego rodzaju możliwych awarii komponentu
(na przykład zerwania połączenia sieciowego) decydujemy, jak system ma na tę awa-
rię reagować (na przykład — powiadamiać użytkownika o zaistniałej sytuacji). Każdą
z tych decyzji dokumentujemy w postaci przypadku użycia, wzbogacającego zestaw
„normalnych" przypadków zidentyfikowanych na etapie zbierania wymagań. Za-
uważmy jednak, że wymaganie odporności na awarie może skutkować zmianą w pro-
jekcie systemu zamiast definiowania kolejnego przypadku użycia — przykładowo
podsystem Routi ngSubsystem stanie się odporny na zanik połączenia sieciowego,
jeśli kompletna trasa pobranie zostanie z podsystemu PI anni ngSubsystem przed
rozpoczęciem podróży.

Ogólnie rzecz biorąc, zdarzeniem wyjątkowym jest błąd zaistniały w czasie wykonywania
systemu. Zdarzenia takie mogą być powodowane trzema podstawowymi przyczynami. Oto one.

• Awaria sprzętu. Sprzęt starzeje się i od pewnego momentu zaczyna być coraz bardziej
zawodny; awaria dysku twardego może prowadzić do nieodwracalnej utraty danych,
awaria routera może spowodować nagłe zerwanie połączenia.
• Zmiany w środowisku operacyjnym. Praca systemu uzależniona jest od wielu ele-
mentów jego środowiska. System mobilny może utracić połączenie z serwerem, gdy
antena modemu znajdzie się poza zasięgiem nadajnika; awaria zasilania może mo-
mentalnie unieruchomić system, jeśli komputer pozbawiony jest możliwości na-
tychmiastowego, automatycznego przełączenia się na zasilanie bateryjne.
• Błędy w oprogramowaniu. Taki błąd, nawet gdy występuje tylko w obrębie jednego
komponentu, może potencjalnie załamać cały system. Mimo iż całkowite wyelimi-
nowanie błędów z oprogramowania jest niedoścignionym ideałem, można jednak
uodpornić każdy podsystem na przewidywane błędy, które potencjalnie mogłyby
wystąpić w nim i w pozostałych podsystemach.

Obsługa sytuacji wyjątkowych to sposób traktowania takich sytuacji przez system i me-
chanizm realizujący owo traktowanie. I tak w przypadku wprowadzenia przez użytkownika
błędnych danych najbardziej rozsądną reakcją wydaje się wyświetienie komunikatu wyjaśniają-
cego użytkownikowi przyczynę błędu i zachęcającego do ponownego wprowadzenia danych,
tym razem poprawnych. W przypadku awarii sieci system może zachować informację o swym
bieżącym w stanie, by wznowić pracę, gdy sieć ponownie zacznie prawidłowo funkcjonować.
Rozpatrzmy jako przykład system nawigacyjny samochodu, pobierający na żądanie
informacje z centralnego komputera; gdy samochód wjedzie do tunelu, transfer informacji
może zostać przerwany, w wyniku czego warstwa sieciowa8 nie będzie mogła sformować kolej-
nego pakietu, co zasygnalizuje przez zgłoszenie wyjątku („niespodziewane zamknięcie gniazda").

8
Poszczególne warstwy sieciowego modelu odniesienia OSI-ISO przedstawione są na rysunku 6.10
— przyp. tłum.
7.4. Aktywności realizacji celów projektowych 325

Warstwa transportowa (korzystająca z usług warstwy sieciowej) może wówczas propagować


ów wyjątek do wyższych warstw łub próbować poradzić sobie z zaistniałą sytuacją (bez wiedzy
warstw wyższych), na przykład podejmując kilka prób ponowienia transmisji w pewnych od-
stępach czasu; w międzyczasie operować będzie na poprzednio otrzymanych danych.
Programiści, identyfikując warunki graniczne, analizują możliwe awarie każdego kom-
ponentu i określają sposób ich obsługi: mogą zaprojektować komponent tak, by samodzielnie
radził sobie ze „swymi" awariami albo uwzględnić możliwość sygnalizowania przezeń awarii
korzystającym z niego komponentom — na tę drugą okoliczność należy skonstruować odpo-
wiedni graniczny przypadek użycia. Zauważmy, że na etapie projektowania systemu rozwa-
żamy awarie jedynie na poziomie komponentów; w rozdziale 9. „Projektowanie obiektów:
specyfikowanie interfejsów" zajmiemy się obsługą sytuacji wyjątkowych na poziomie poszcze-
gólnych obiektów.
Projektowanie niezawodnych systemów jest trudną sztuką, którą można jednak do pew-
nego stopnia upraszczać, ograniczając nieco funkcjonalność docelowego systemu. Przykładowo
w systemie MyTri p sformułowaliśmy wymagania, by połączenie komputera pokładowego
z serwerem było zawsze możliwe przy rozpoczynaniu podróży oraz by zmiana planowanej
trasy możliwa była tylko w warunkach istnienia tego połączenia.
Wprowadzenie granicznych przypadków użycia (patrz rysunek 7.11) do modelu systemu
MyTri p stanowi kolejną modyfikację tego systemu. Dodaliśmy trzy przypadki użycia: Manage
"-•Drivers obejmujący dodawanie, modyfikowanie i usuwanie profili kierowców, ManageMaps
związany z dodawaniem, modyfikowaniem i usuwaniem map używanych do konstruowa-
nia tras i ManageServer ilustrujący rutynowe konfigurowanie, uruchamianie i zamykanie sys-
temu. W tabeli 7.6 przedstawiamy przypadek użycia StartServer, dołączany jako część przy-
padku ManageServer.

Rysunek 7.11. Administracyjne przypadki użycia dla systemu MyTri p. Przypadek użycia ManageDrivers
wywoływany jest w celu dodawania, usuwania lub modyfikowania danych dotyczących kierowców (nazw,
haseł, historii transakcji, generowanych kluczy szyfrowania), przypadek ManageMaps — w celu dodawania,
usuwania i modyfikowania m a p służących do generowania tras, natomiast przypadek ManageServer
obejmuje wszelkie funkcje związane z uruchamianiem i zamykaniem serwera
326 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Tabela 7.6. Przypadek użycia StartServer systemu MyTri p

Nazwa przypadku użycia StartServer

Warunek wstępny 1. Aktor PI anni ngServi ceAdmi ni s t r a t o r zalogowany jest do serwera


systemu.

Przepływ zdarzeń 2. Aktor PI anni ngServi ceAdmi ni s t r a t o r wydaje polecenie


s t a r t P l a n n i ngServi ce.

3. Jeśli poprzednie wykonywanie usługi PI anni ngServi ce zakończyło


się normalnie, serwer odczytuje listę zarejestrowanych kierowców
(Dri v e r ) oraz indeks m a p (Map) i aktywnych tras (Tri p). Jeśli
p o p r z e d n i e wykonywanie usługi PI anni ngServi ce zakończyło
się awaryjnie, system i n f o r m u j e o tym fakcie a d m i n i s t r a t o r a
(PI anni ngServi ceAdmi ni s t r a t o r ) i rozpoczyna sprawdzanie
spójności danych w bazie MapDBStore.

Warunek końcowy 4. Usługa PI anni ngServi ce jest aktywna i oczekuje na połączenia


ze strony instancji podsystemu Routi ngSubsystem.

Rewizja modelu przypadków użycia, w postaci dodania trzech przypadków, nie wpłynęła
na dekompozycję systemu, dodaliśmy jednak nowe przypadki użycia do istniejących podsys-
temów: podsystem MapDBStoreSubsystem obarczony został obowiązkiem sprawdzania, czy jego
poprzednie użytkowanie zakończyło się poprawnym zamknięciem — jeżeli nie, musi on wykonać
kolejne zadanie: sprawdzić spójność danych i w razie potrzeby przywrócić ich poprawną po-
stać. Oczywiście, fakt ten znajduje swe odzwierciedlenie w opisie podsystemu (patrz tabela 7.7).

Tabela 7.7. Zrewidowany opis podsystemu MapDBStoreSubsystem, stosownie do nowego przypadku


użycia S t a r t S e r v e r (patrz tabela 7.6). Zmiany wyróżniono kursywą

MapDBStoreSubsystem Podsystem odpowiedzialny za przechowywanie map i tras w bazie


p o d s y s t e m u PI anni ngSubsystem. Podsystem ten zdolny jest do
równoczesnej obsługi wielu kierowców i agentów planowania. Podczas
rozpoczynania pracy podsystem sprawdza, czyjego poprzednie użycie
zakończyło się poprawnym zamknięciem —jeśli nie, dokonuje sprawdzenia
spójności map (MapJ i tras (Tri p) i w razie potrzeby podejmuje działania
naprawcze.

7.4.7. Weryfikowanie projektu systemu


Podobnie jak analiza wymagań, tak i projektowanie systemu jest aktywnością ewolucyjną i ite-
ratywną. W przeciwieństwie jednak do analizy wymagań, w projektowaniu systemu nie uczestni-
czy żaden zewnętrzny agent, weryfikujący kolejne iteracje i przyczyniający się do ciągłej po-
prawy jakości produktów. Sukcesywna poprawa jakości jest jednak z oczywistych powodów
jak najbardziej pożądana — można ją uzyskiwać poprzez okresową weryfikację projektu, do-
konywaną przez programistów przy udziale menedżerów. Istnieje wiele możliwości wyboru
substytutu wspomnianego agenta zewnętrznego: rolę tę mogą spełniać programiści niezaan-
gażowani w projektowanie bądź (na zasadzie wzajemności) programiści uczestniczący w in-
nym projekcie. Przedsięwzięcie takie ma jednak szansę powodzenia tylko wówczas, gdy wery-
fikatorzy faktycznie działają z intencją wykrywania i raportowania problemów.
7.4. Aktywności realizacji celów projektowych 327

W uzupełnieniu do spełnienia zidentyfikowanych celów projektowych musimy także


upewnić się, że model projektu systemu jest poprawny, kompletny, spójny, realistyczny i czytelny.
Model projektu systemu jest poprawny, jeśli jest odwzorowaniem modelu analitycznego,
o czym możemy przekonać się, zadając następujące pytania:

• Czy istnienie każdego podsystemu da się uzasadnić przez przypadek użycia lub wy-
maganie pozafunkcyjne?
• Czy każdy przypadek użycia może być odwzorowany w podzbiór zdefiniowanych
podsystemów?
• Czy każdy cel projektowy można wywieść z wymagania pozafunkcyjnego?
• Czy każde wymaganie pozafunkcyjne zostało uwzględnione w modelu projektu
systemu?
• Czy dla każdego aktora zdefiniowano zasady kontroli dostępu?
• Czy wszystkie zasady kontroli dostępu spójne są z wymaganiami pozafunkcyjnymi
dotyczącymi bezpieczeństwa i zabezpieczeń?

Model jest kompletny, jeśli uwzględnia wszystkie wymagania funkcyjne i pozafunkcyjne


oraz wszelkie problemy zauważone w trakcie projektowania. Następujące pytania pomogą nam
ocenić kompletność modelu:

• Czy obsługiwane są wszystkie warunki graniczne?


• Czy dokonany został przegląd („wędrówka") wszystkich przypadków użycia w celu
zidentyfikowania braków funkcjonalnych w modelu projektu systemu?
• Czy każdy przypadek użycia został dostatecznie zweryfikowany i czy w każdym z nich
występuje przynajmniej jeden obiekt sterujący?
• Czy w modelu uwzględniono wszystkie aspekty projektowania systemu (odwzoro-
wanie komponentów w węzły sprzętowe, przechowywanie obiektów trwałych, kon-
trolę dostępu, wykorzystanie istniejącego kodu, warunki graniczne)?

• Czy wszystkie podsystemy zostały jasno zdefiniowane?

Model jest spójny, jeśli jest niesprzeczny, co sprowadza się do następujących kwestii:

• Czy konfliktujące cele projektowe zostały opatrzone różnymi priorytetami?


• Czy żaden z celów projektowych nie jest sprzeczny z wymaganiami pozafunkcyjnymi?
• Czy każdy podsystem i każda klasa opatrzone są unikalną nazwą?
• Czy kolekcje obiektów wymieniane są między podsystemami w sposób spójny?
Model jest realistyczny, jeśli opisywany przez niego system jest możliwy do zaimple-
mentowania. O możliwości tej decydują między innymi następujące kryteria:

• Czy w systemie przewidywane jest użycie nowych technologii i (lub) komponentów?


Jeśli tak, to czy oceniona została ich adekwatność i solidność? W jaki sposób?
328 Rozdział 7. • Projekt systemu: realizacja celów projektowych

• Czy wymagania dotyczące wydajności i niezawodności zostały przeanalizowane


w kontekście dekompozycji systemu?
• Czy uwzględnione zostały potencjalne konsekwencje współbieżności (przeciążenia,
zastój)?

Wreszcie, model jest czytelny, jeśli zrozumiały jest dla programistów nieuczestniczących
w jego tworzeniu. Czytelność modelu przekłada się na następujące pytania:

• Czy nazwy podsystemów są zrozumiałe?


• Czy encje (podsystemy, klasy) o podobnych nazwach reprezentują podobne koncepcje?
• Czy wszystkie encje opisane zostały na tym samym stopniu szczegółowości?

W przypadku wielu projektów daje się zauważyć częściowe nakładanie się projektowa-
nia systemu i jego implementowania; przykładowo w celu oceny i weryfikacji nowych tech-
nologii tworzy się prototypy wybranych podsystemów, zanim jeszcze ustabilizuje się architek-
tura systemu. Konsekwencją tego jest wiele przeglądów cząstkowych zamiast pojedynczego
przeglądu klienckiego związanego z akceptacją modelu analitycznego. Mimo iż takie po-
dejście sprawia, że projekt staje się bardziej elastyczny, wymaga od programistów bardziej
uważnego traktowania otwartych problemów. Rozwiązywanie wielu trudnych problemów
odkłada się na później nie dlatego, że są trudne, lecz ze względu na to, iż zdają się burzyć
względny ład istniejący w procesie.

7.5. Zarządzanie projektowaniem systemu


W tej sekcji przedyskutujemy zagadnienia związane z zarządzaniem aktywnościami etapu
projektowania systemu. Podobnie jak w analizie wymagań, podstawowym wyzwaniem tego
zarządzania jest utrzymywanie szeroko rozumianej spójności, niezależnie od tego, jak wiele
zasobów znajduje się w użyciu. Dzięki owej spójności efektem projektu będzie architektura
i zbiór interfejsów jednoznacznie opisujące spójny system, w sposób zrozumiały dla pojedyn-
czej osoby. Rozpoczniemy od opisania szablonu dokumentu przedstawiającego wyniki pro-
jektowania (patrz sekcja 7.5.1), po czym zajmiemy się rolami przypisywanymi w związku
z projektowaniem systemu (patrz sekcja 7.5.2) i problemami komunikacyjnymi w projekto-
waniu systemu (patrz sekcja 7.5.3). Następnie omówimy rolę menedżera projektu w kontek-
ście iteracyjnej natury projektowania systemu (patrz sekcja 7.5.4).

7.5.1. Dokumentowanie projektu systemu


Rezultaty projektowania systemu udokumentowane zostają w publikacji zwanej dokumentem
projektu systemu, w skrócie SDD (System Design Document). Dokument SDD opisuje cele
projektowe, dekompozycję systemu (w formie diagramów klas UML), odwzorowanie sprzę-
towe i programowe komponentów systemu (w postaci diagramów wdrażania UML), zarzą-
dzanie danymi, kontrolę dostępu, mechanizmy przepływu sterowania i warunki graniczne.
SDD stanowi podstawę do definiowania interfejsów między zespołami programistów i służy
jako punkt odniesienia przy wszelkich decyzjach dotyczących poziomu architektury systemu.
7.5. Zarządzanie projektowaniem systemu 329

SDD adresowany jest do menedżerów projektu, architektów systemu (czyli programistów


uczestniczących w jego projektowaniu) i programistów implementujących poszczególne pod-
systemy. Przykładowy szablon SDD przedstawiamy w poniższej ramce.

Dokument projektu systemu


1. Wstęp
1.1. Przeznaczenie systemu
1.2. Cele projektowe
1.3. Definicje, akronimy i skróty
1.4. Odwołania
1.5. Podsumowanie
2. Architektura obecnie używanego oprogramowania
3. Architektura proponowana dla nowego oprogramowania
3.1. Streszczenie
3.2. Dekompozycja systemu
3.3. Odwzorowanie sprzętowe i programowe k o m p o n e n t ó w systemu
3.4. Zarządzanie danymi trwałymi
3.5. Kontrola dostępu i bezpieczeństwo
3.6. Globalny przepływ sterowania
3.7. Warunki graniczne
4. Usługi świadczone przez podsystemy
5. Słownik

Pierwsza sekcja dokumentu SDD jest wprowadzeniem. Jej przeznaczeniem jest dostar-
czenie krótkiego opisu architektury oprogramowania i celów projektowych. Zawiera także
odwołania do innych dokumentów (między innymi powiązanego dokumentu RAD) i infor-
mację o identyfikowalności (powołanie się na istniejące systemy czy ograniczenia dotyczące
architektury).
Sekcja druga opisuje architekturę istniejącego systemu, który ma zostać zastąpiony przez
tworzony system. Jeśli nowy system tworzony jest od zera, można w tym miejscu zawrzeć
przegląd architektur stosowanych w podobnych systemach. Celem tej części jest jawne wyar-
tykułowanie informacji, na jakich opierają się architekci systemu, przyjmowanych przez nich
założeń oraz problemów, jakie nowy system ma rozwiązywać.
Trzecia sekcja, poświęcona modelowi projektu nowego systemu, podzielona jest na siedem
następujących części:

• Streszczenie — prezentuje architekturę systemu „z lotu ptaka" i zawiera krótki opis


elementów funkcjonalnych każdego podsystemu.
• Dekompozycja systemu — tu znajduje się opis podziału systemu na podsystemy
i przypisanie zakresu odpowiedzialności do każdego podsystemu; jest to główny pro-
dukt etapu projektowania systemu.
330 Rozdział 7. • Projekt systemu: realizacja celów projektowych

• Odwzorowanie sprzętowe i programowe komponentów systemu — zawiera opis


odwzorowania podsystemów w komponenty sprzętowe i komponenty „z półki",
wylicza także problemy związane z rozproszeniem systemu między wiele węzłów
i wynikającymi stąd problemami dotyczącymi współbieżności i synchronizacji.
• Zarządzanie danymi trwałymi — opisuje dane zapamiętywane między kolejnymi
uruchomieniami systemu i infrastrukturę realizującą zarządzanie tymi danymi —
wybór systemu bazy danych, schematy danych i hermetyzację bazy danych wobec
innych podsystemów.
• Kontrola dostępu i bezpieczeństwo — tu znajduje się opis modelu dostępu do
systemu w formie macierzy dostępu, jak również szczegóły systemu zabezpieczeń:
mechanizm uwierzytelniania, algorytm szyfrowania, zarządzanie kluczami szyfrują-
cymi i tym podobne.
• Globalny przepływ sterowania — w tej części opisywana jest implementacja globalnej
strategii przepływu sterowania, jak też inicjowanie żądań i synchronizowanie działań
poszczególnych podsystemów. Ponadto powinien się tu znaleźć również opis pro-
blemów związanych ze współbieżnością.
• Warunki graniczne — tu opisane są procedury uruchamiania i zamykania systemu
oraz jego zachowanie się w sytuacjach wyjątkowych. (Jeśli zidentyfikowane zostały
nowe przypadki użycia związane z administrowaniem systemem, powinny zostać
dołączone do dokumentu RAD, nie powinno się ich tutaj opisywać).

W sekcji czwartej opisywane są usługi oferowane przez każdy z podsystemów. Mimo iż


w pierwszych wersjach dokumentu SDD sekcja ta pozostaje pusta albo niekompletna, stanowi
ona punkt odniesienia dla konkretnych zespołów przy ustalaniu granic poszczególnych pod-
systemów. Na podstawie tej sekcji definiowane są interfejsy podsystemów, opisywane później
w dokumencie projektu obiektów.
Dokument SDD sporządzany jest po dokonaniu wstępnej dekompozycji systemu — ar-
chitekci systemu nie powinni czekać z jego opublikowaniem, aż do sformułowania wszyst-
kich decyzji projektowych. Podobnie jak dokument RAD, dokument SDD z chwilą opubli-
kowania zyskuje status „linii bazowej", lecz podlegać będzie rozmaitym zmianom, w miarę
odkrywania nowych problemów i podejmowania nowych decyzji — zmiany te podlegają
formalnemu procesowi zarządzania konfiguracją, każda wprowadzona zmiana powinna być
opatrzona datą i przypisana konkretnemu autorowi.

7.5.2. Przydzielanie odpowiedzialności


W przeciwieństwie do analizy wymagań, projektowanie systemu odbywa się w kręgu progra-
mistów — klient i użytkownicy schodzą na plan dalszy. Wiele aktywności związanych z pro-
jektowaniem systemu powodować może jednak konieczność wprowadzania poprawek do
modelu analitycznego, co nie powinno się odbywać bez konsultacji z klientem i użytkowni-
kami. W przypadku typowych, złożonych systemów projektowanie koncentruje się wokół ze-
społu architektonicznego — jest to zespół międzyfunkcyjny, złożony z architektów definiują-
cych dekompozycję systemu oraz niektórych programistów implementujących ten system.
7.5. Zarządzanie projektowaniem systemu 331

Ważne jest, by projektowaniem systemu zajmowali się ludzie, którzy w naturalny sposób
ponosić będą konsekwencje podejmowanych przez siebie decyzji. Zespół architektoniczny
rozpoczyna swą pracę, gdy tylko ustabilizuje się model analityczny, i funkcjonuje, aż do fazy
integrowania systemu; stanowi to zachętę dla jego członków do przewidywania potencjalnych
problemów, jakie pojawić się mogą w związku z tym integrowaniem.
Oto kilka najważniejszych ról, jakie przypisane zostają członkom zespołu architek-
tonicznego:

• architekt — jest kierownikiem zespołu architektonicznego i pełni główną rolę


w projektowaniu systemu, nadzorując zachowanie spójności w obszarze decyzji
projektowych i w stylach interfejsów podsystemów. Jest także odpowiedzialny za
zachowanie spójnego charakteru projektu pod względem zarządzania konfiguracją
oraz zdefiniowanie strategii integrowania systemu. Rola architekta jest rolą typowo
integracyjną, opierającą się na informacjach otrzymywanych od zespołów funkcyjnych.
• łącznik architektoniczny — tę rolę pełni każdy z członków zespołu architektonicz-
nego, będący reprezentantem zespołu funkcyjnego. Łącznik odpowiedzialny jest
za przepływ informacji między zespołem architektonicznym a „swoim" zespołem
oraz za negocjowanie zmian w ramach interfejsu podsystemu opracowywanego
przez ten zespół. W fazie projektowania systemu łącznicy skupiają się na usługach
oferowanych przez podsystemy, w fazie implementowania interesować ich będzie
natomiast spójność API.
• role redaktora dokumentacji, menedżera konfiguracji i weryfikatora są w fazie pro-
jektowania systemu identyczne z rolami na etapie analizy wymagań (patrz sekcja 5.5.2).

Liczba podsystemów powstających w wyniku dekompozycji determinuje liczebność


zespołu architektonicznego. Dla systemów szczególnie skomplikowanych tworzonych jest
kilka zespołów architektonicznych, po jednym dla każdego poziomu abstrakcji. W tym ostat-
nim przypadku każdy zespół powinien posiadać własnego architekta, niezależnie od tego po-
winna być wyznaczona rola głównego architekta na szczeblu projektu, by zdefiniowana ar-
chitektura systemu była spójna i zrozumiała dla pojedynczego człowieka.

7.5.3. Komunikacja w projektowaniu systemu


Komunikacja między uczestnikami projektowania systemu nie stanowi już wyzwania tak wiel-
kiego, jak na etapie analizy wymagań: jasny jest już obraz funkcjonalności systemu, uczestnicy
mają już podobne wyobrażenie o systemie, no i zdążyli poznać się lepiej. Mimo to, komuni-
kowanie się między nimi wciąż nie jest łatwe za sprawą nowych źródeł komplikacji. Oto one.

• Rozmiar. Liczba problemów, z jakimi przychodzi się zmagać, rośnie w miarę postępu
w projektowaniu. Zwiększa się liczba elementów, jakimi manipulować muszą pro-
gramiści: każdy aspekt funkcjonalny wymaga uwzględnienia wielu operacji na wielu
obiektach. Dodatkową komplikację stanowi analizowanie wielu projektów i rozpo-
znawanie szczegółów nowych technologii.
332 Rozdział 7. • Projekt systemu: realizacja celów projektowych

• Zmiany. Dekompozycja systemu i interfejsy poszczególnych podsystemów wciąż


intensywnie ewoluują, podobnie jak terminologia używana przez programistów do
określenia różnych elementów systemu. Bywa, że wskutek dynamiki tych zmian pro-
gramiści nie zdążą uzgodnić ze sobą nowych koncepcji, co prowadzi do wielu niepo-
rozumień i kolejnych komplikacji.
• Poziom abstrakcji. Dyskusję na temat wymagań można było uczynić wysoce kon-
kretną, dzięki używaniu makiet, prototypów i analogii z istniejącymi systemami.
Podobnie konkretna staje się dyskusja na temat implementacji, gdy znane są wyniki
integracji i testowania. Dyskutowanie na temat projektowania systemu rzadko bywa
tak konkretne z tej prostej przyczyny, że większość konsekwencji podejmowanych
decyzji projektowych odczuwana jest dopiero na etapie implementacji i testowania.
• Niechęć do konfrontacji z problemami. Wysoki poziom abstrakcji większości dyskusji
stwarza okazję do odkładania „na później" rozstrzygania wielu trudnych, konkret-
nych kwestii — typowym rozwiązaniem problemu bywa wówczas stwierdzenie w ro-
dzaju „wrócimy do tego przy implementowaniu". Mimo iż pożądane jest opóźnianie
niektórych decyzji, na przykład dotyczących wyboru konkretnej struktury danych
czy konkretnego algorytmu, decyzje mające wpływ na kształt dekompozycji systemu
i interfejsy podsystemów nie powinny być odkładane „na później".
• Sprzeczne cele i kryteria. Często zdarza się tak, że różni programiści przyjmują
odmienne kryteria optymalizacji. Programista doświadczony w pracy nad interfej-
sami użytkownika postrzegać będzie czas reakcji systemu jako główny cel optymali-
zacji, podczas gdy dla programisty parającego się tematyką bazodanową najbardziej
istotna będzie przepustowość transmisji danych. Tego typu konfliktowe cele, zwłasz-
cza gdy nie są wyartykułowane jawnie, mogą kierować dekompozycję na różne tory
i prowadzić do kłopotliwych niespójności w jej zakresie.

Techniki radzenia sobie z opisanymi komplikacjami podobne są do tych, jakie rozważa-


liśmy w związku z analizą wymagań (patrz sekcja 5.5.3):

• Określenie priorytetów poszczególnych celów projektowych i uczynienie ich publicznie


wiadomymi (patrz sekcja 6.4.2). Dzięki temu programiści zaangażowani w projek-
towanie łatwiej będą mogli zaplanować czas na realizację poszczególnych celów.
Zestaw celów projektowych pełni także rolę obiektywnego punktu odniesienia,
względem którego oceniane są wszelkie podejmowane decyzje.
• Udostępnienie bieżącego kształtu dekompozycji wszystkim zainteresowanym. Jednym
ze sposobów udostępnienia tej informacji jest rozpowszechnianie „żywych" doku-
mentów w internecie lub intranecie. Programiści mogą także kontrolować na bie-
żąco dokonywane zmiany za pomocą różnych narzędzi dedykowanych zarządzaniu
konfiguracją.
• Utrzymywanie aktualnego słownika. Podobnie jak w przypadku analizy, oficjalne,
publiczne źródło terminologii zmniejsza ryzyko nieporozumień. Każdy zidentyfiko-
wany podsystem powinien, oprócz sugestywnej nazwy, posiadać także jasną i zwięzłą
definicję — diagramy UML, które z konieczności ograniczają się tylko do nazw
7.5. Zarządzanie projektowaniem systemu 333

podsystemów, nie są wystarczającym źródłem efektywnej komunikacji. Podob-


nymi definicjami powinny być opatrywane wszystkie klasy wchodzące w skład
poszczególnych podsystemów.
• Konfrontacja z problemami projektowymi. Odkładanie decyzji projektowych „na
później" może być korzystne w sytuacji, gdy do podjęcia konkretnej decyzji po-
trzebne jest uzyskanie dodatkowych informacji. Niestety, ta zasada często wykorzy-
stywana bywa jako pretekst do ucieczki przed trudnymi problemami projektowymi.
Zatem przed decyzją o odłożeniu konkretnego problemu na później, warto prze-
analizować znane warianty jego rozwiązania oraz wpływ na dekompozycję.
• Iterowanie. Wybiórcze „wybieganie w przód", w fazę implementacji, może przyczynić
się do usprawnienia projektu systemu. Przykładowo nowe funkcje, jakie pojawiły się
w nowej wersji komponentu „z półki", mogą zostać ocenione przez zaimplemento-
wanie „pionowego prototypu" (patrz sekcja 7.5.4) pod kątem ich przydatności w obec-
nym projekcie.

Niezależnie od tego, jak starannie i jakim kosztem wykonany został projekt systemu,
kształt dekompozycji systemu i interfejsy podsystemów prawie na pewno zmieniać się będą
w fazie implementacji. Przyczyną tego będzie nie tylko coraz lepsze rozumienie systemu przez
programistów i odkrywane w związku z tym możliwości ulepszenia projektu, lecz także
pojawienie się nowych technologii i nowych pakietów zawierających gotowe rozwiązania.
Programiści powinni być świadomi tego faktu i muszą zarezerwować sobie trochę czasu na
uaktualnienie dokumentu SDD przed przystąpieniem do integrowania systemu.

7.5.4. Iteracje projektowania systemu


Podobnie jak wymagania, także projekt systemu doznaje kolejnych przeobrażeń, iteracji
i zmian. Owe zmiany powinny być jednak kontrolowane, w celu uniknięcia chaosu, tym bar-
dziej prawdopodobnego, im bardziej złożony jest projekt i im większą angażuje liczbę uczest-
ników. W fazie projektowania systemu wyróżnić możemy trzy rodzaje iteracji. Pierwszy z nich
odnosi się do iteracji związanych z ogólnymi decyzjami na samym początku procesu. Drugi
charakteryzuje iteracje związane z interfejsami podsystemów, wynikające z nowej wiedzy
zdobywanej w związku z oceną prototypów, iteracje trzeciego rodzaju wynikają natomiast
z błędów i przeoczeń odkrywanych w późniejszych fazach i wymagających powrotu do defi-
niowania interfejsów, a często i nawet samej dekompozycji.
Iteracje pierwszego rodzaju najefektywniej realizowane są w formie „burzy mózgów"
prowadzonej bądź to w formie osobistego spotkania uczestników, bądź też w formie kon-
ferencji internetowej. Definicje związane z systemem są wówczas wysoce płynne, programiści
nie mają jeszcze dobrego wyobrażenia o systemie jako całości, zatem sprawna komunikacja
staje się najważniejsza, ważniejsza niż formalności i procedury. W zespołowej organizacji
projektu wstępna dekompozycja systemu dokonywana jest dość wcześnie, zanim jeszcze
zakończy się budowanie modelu analitycznego. Kształt tej dekompozycji jest wówczas o tyle
istotny, iż staje się podstawą do przydzielania zakresu odpowiedzialności poszczególnym ze-
społom. Wszechstronna eksploracja problemów i niezbędne zmiany są wtedy jak najbardziej
pożądane, choćby tylko dla lepszego zrozumienia kolejnych szczegółów systemu i bieżącej po-
staci projektu. Spontaniczny charakter tych iteracji czyni zbędną jakąkolwiek biurokrację.
334 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Iteracje drugiego rodzaju zmierzają do rozwiązywania trudnych i dobrze zdefiniowanych


problemów, takich jak na przykład wybór odpowiedniej technologii czy konkretnego dostawcy.
Dekompozycja systemu jest już raczej ukształtowana — w idealnym przypadku niezależna
jest od konkretnego dostawcy o konkretnej technologii — i większość wątpliwości związana
jest wówczas z przydatnością określonych pakietów czy komponentów dla tworzonego systemu.
W tym okresie programiści często tworzą „pionowe prototypy" 9 w celu zweryfikowania owej
przydatności. Umożliwia to wczesne wykrywanie problemów związanych z przepływem ste-
rowania. Ponownie zbytnia formalizacja procesu nie jest tu wskazana, wystarczająca okazuje
się lista otwartych problemów wraz z informacjami o statusie każdego z nich.
Iteracje trzeciego typu stanowią remedium na problemy projektowe wykrywane w póź-
nej fazie projektu. Mimo iż programiści woleliby takich iteracji raczej unikać, jako że są kosz-
towne i stanowią ryzyko wprowadzenia wielu błędów do systemu, to jednak muszą być na nie
przygotowani. Przygotowanie to wyraża się między innymi w postaci starannego dokumen-
towania zależności między podsystemami, racjonalności kryjącej się za ich interfejsami oraz
ewentualnych rozwiązań „na skróty", które prawdopodobnie okażą się niepoprawne po wpro-
wadzeniu zmian. Wszelkie zmiany powinny być kategorycznie zarządzane, w formie procesu
podobnego do śledzenia zmian w specyfikacji wymagań.
Postępującą stabilizację dekompozycji systemu łatwiej osiągnąć, wprowadzając kon-
cepcję „okna projektowego". „Okno" to oznacza dopuszczalny przedział czasu, przez który
kluczowe problemy mogą pozostawać nierozwiązane. Przykładowo wybór platformy sprzę-
towej i programowej dla nowego systemu powinien być dokonany jak najwcześniej, tak aby
decyzje dotyczące zakupu sprzętu i oprogramowania mogły zostać podjęte i wykonane w czasie
realizacji projektu. Problemy dotyczące wyboru określonego algorytmu czy konkretnej struk-
tury danych można jednak pozostawić otwarte, aż do czasu integrowania podsystemów, kiedy
to programiści będą mogli je skutecznie rozwiązać, opierając się na wynikach testowania
wydajności. Zanim „okno" dla konkretnego problemu zostanie zamknięte, problem ten musi
zostać rozwiązany, a powrót do niego nastąpić może dopiero w następnej iteracji.
W warunkach szybkich innowacji technologicznych wiele związanych z nimi zmian
można lepiej przewidywać, gdy w firmie istnieje dział dedykowany zarządzaniu technologiami.
Menedżerowie technologiczni śledzą wówczas zmiany na rynku technologii, oceniają je i gro-
madzą wiedzę, która okaże się użyteczna, gdy przyjdzie wyboru komponentów. Często zmiany
technologiczne następują tak szybko, iż firmy nie są ich do końca świadome.

7.6. Analiza przypadku — system ARENA


Pokażemy teraz, jak opisywane w tym rozdziale metody i koncepcje zastosować można do syste-
mu ARENA. Rozpoczniemy od zidentyfikowania celów projektowych dla tego systemu i do-
konamy jego wstępnej dekompozycji. Potem dokonamy wyboru sprzętu i oprogramowania oraz
zdefiniujemy strategię przechowywania trwałych danych, założenia kontroli dostępu i global-
ną strukturę sterowania. Na końcu przyjrzymy się warunkom granicznym systemu ARENA.

9
Prototyp pionowy (wertykalny) to kompletna implementacja wąskiego wycinka funkcjonalności,
zwykle związanej z określonym przypadkiem użycia i jego obiektami brzegowymi, encji i sterującymi.
Prototyp poziomy (horyzontalny) jest natomiast częściową implementacją szerokiego zakresu funkcjo-
nalności, na przykład implementacją jedyne obiektów brzegowych obszernego zbioru przypadków użycia.
7.6. Analiza przypadku — system ARENA 335

7.6.1. Identyfikowanie celów projektowych


Cele projektowe określają zróżnicowanie priorytetów poszczególnych cech systemu. Wywodzą
się z wymagań pozafunkcyjnych, zdefiniowanych w czasie zbierania wymagań oraz z celów
technicznych i menedżerskich określonych przez projekt.
Głównym klientem systemu ARENA jest ArenaOperator, dostarczający środki dla udo-
stępnienia tego systemu określonej społeczności. Jest on graczem (PI ayer) posiadającym kwa-
lifikacje administrowania systemem czy nawet kwalifikacje programisty. Możliwość reklamo-
wania własnych produktów daje mu częściową rekompensatę kosztów związanych z systemem.
Przewidujemy ponadto, że poszczególni aktorzy ArenaOperator będą tworzyć własne społecz-
ności i integrować z systemem ARENA nowe gry, jak również zgłaszać zapotrzebowanie na
sukcesywne ulepszenia systemu. Reklamy i ogłoszenia nie są jednak zasadniczym przeznacze-
niem systemu ARENA. Na podstawie powyższych obserwacji oraz na podstawie deklaracji pro-
blemu dla systemu ARENA identyfikujemy następujące cele projektowe.

• Niskie koszty operacyjne. Aby zmniejszyć zapotrzebowanie na reklamy ze strony


klienta (ArenaOperator) jako środek rekompensowania jego kosztów operacyjnych
(związanych z infrastrukturą sprzętową, siecią, administrowaniem i tym podobnymi),
należy dążyć do tego, by koszty te były jak najmniejsze. Prowadzi to do wyboru
komponentów darmowych lub kategorii open source. Ten cel projektowy stanowi
rozwinięcie wymagania pozafunkcyjnego „Niski koszt użytkowania".
• Duża dostępność. Wartość systemu ARENA mierzona jest liczbą graczy chętnych do
uczestniczenia w turniejach. Nieoczekiwane awarie systemu czy niespodziewane
przerwanie pasjonującego pojedynku — zdecydowanie nie są to przyjemne dla gracza
doświadczenia, zachęcające do udziału w kolejnych turniejach. Ten cel projektowy
nie znajduje swej genealogii w deldaracji problemu, lecz jest niezbędny i oczywisty,
jeśli system ARENA zachowywać ma swą atrakcyjność i zdobywać nowe rzesze graczy.
• Skalowalność pojmowana w kategoriach liczby graczy i równoczesnego prowadzenia
wielu turniejów. Reaktywność systemu ARENA nie może pogarszać się w zauważalnym
stopniu wraz z rosnącą liczbą równocześnie połączonych graczy (Player). Arena
"-•Operator powinien mieć w związku z tym możliwość zwiększenia mocy systemu
poprzez dodanie nowych węzłów sprzętowych, gdy zdarzy się taka potrzeba. Ten cel
projektowy stanowi rozwinięcie wymagania pozafunkcyjnego „Skalowalność"
w deklaracji problemu.
• Łatwość dodawania nowych gier. Niektóre gry, takie jak szachy, są rozrywką po-
nadczasową. Tradycyjne rozrywki uzupełniane są rosnącą dynamicznie ofertą roz-
maitych gier komputerowych, ewoluujących w stronę różnych stylów i różnych plat-
form sprzętowych. By system ARENA mógł dotrzymać kroku tej tendencji, musi być
adaptowalny do nowych warunków i powinien umożliwiać łatwe instalowanie nowych
gier. Ten cel projektowy stanowi rozwinięcie wymagania pozafunkcyjnego „Rozsze-
rzalność" w deklaracji problemu.
• Udokumentowanie systemu ARENA dla programistów społeczności open source.
Sporządzenie dokumentacji systemu ARENA ułatwi niezależnym programistom wno-
szenie nowych cech do kodu i ulepszanie go. Wspomniana dokumentacja powinna
336 Rozdział 7. • Projekt systemu: realizacja celów projektowych

obejmować zarówno kod źródłowy systemu (w celu umożliwienia zmian i uspraw-


nień o charakterze niskopoziomowym), jak również jego architekturę (co ułatwi do-
dawanie nowych cech). Ten cel projektowy postulowany jest przez programistów
i menedżerów projektu ARENA, nie przez klienta; zauważmy jednak, że tego rodzaju
cele projektowe wymagają uzgodnień z klientem, mogą bowiem kolidować z do-
myślnymi, choć jeszcze wyraźnie niewyartykułowanymi celami klienta.

7.6.2. Identyfikowanie podsystemów


Identyfikując podsystemy systemu ARENA, oprzemy się najpierw na wymaganiach funkcyjnych
dla tego systemu i na jego modelu analitycznym. Celem tej aktywności jest podział systemu
na samookreślone komponenty, których opracowywaniem mogą zająć się poszczególni pro-
gramiści. Gdy przyjdzie do uwzględnienia kolejnych celów projektowych, związanych między
innymi z kontrolą dostępu czy zarządzaniem danymi trwałymi, będziemy doskonalić i mody-
fikować efekt tej wstępnej dekompozycji.
Rozpoczniemy od rozróżnienia dwóch części systemu ARENA: pierwsza z nich, poświę-
cona organizacji gier, odpowiedzialna będzie za koordynowaniu użytkowników organizujących
instancje systemu, ligę (League) lub turniej (Tournament), druga dedykowana będzie roz-
grywaniu gier i koordynowaniu graczy (PI ayer) rozgrywających poszczególne mecze (Match)
w ramach turniejów (Tournament).
Dla pierwszej, organizacyjnej części, wybierzemy architekturę trójwarstwową (patrz
rysunek 7.12), w ramach której podsystem ArenaCI i e n t dostarcza interfejs czołowy dla
użytkowników aktorów inicjujących przypadki użycia związane z organizacją gier (między
innymi AnnounceTournament, ApplyForTournament i RegisterPlayer). Podsystem ArenaSever
odpowiedzialny jest za kontrolę dostępu i sterowanie współbieżnością oraz delegowanie do
zagnieżdżonych podsystemów zdarzeń odnoszących się do logiki aplikacji — podsystemy te
dedykowane są zarządzaniu użytkownikami, reklamom, turniejom i grom. Najniższa warstwa,
realizowana przez podsystem ArenaStorage, odpowiedzialna jest za przechowywanie wszyst-
kich trwałych obiektów, z wyjątkiem reprezentujących stany meczów (Match).
Dla części drugiej, w której akcja jednego gracza wyzwalać ma akcję drugiego w relatywnie
krótkim czasie, architektura klient-serwer okazuje się niewystarczająca. Synchroniczne za-
chowanie mogłoby być symulowane przez przepytywanie (polling), ze względów skalowalności
i reaktywności systemu wybraliśmy inne rozwiązanie — architekturę peer-to-peer, w ramach
której instancje podsystemu MatchFrontEndPeer dostarczają interfejs użytkownika, zaś instancje
podsystemu GamePeer przechowują informację o stanie rozgrywanych meczów, wymuszając
jednocześnie zachowywanie reguł gry. Dla gier rozgrywanych w czasie rzeczywistym poszcze-
gólne instancje podsystemu MatchFrontEndPeer mogą komunikować się bezpośrednio. Zgodnie
z postulatem niezależności systemu ARENA od konkretnej gry (który to postulat jest jednym z ce-
lów projektowych), ARENA dostarcza uniwersalne środowisko dla podsystemów Match Front
^-EndPeer i GamePeer, podczas gdy elementy specyficzne dla konkretnej gry zgrupowane
są w odrębnych, specjalizowanych komponentach realizujących logikę owej gry. Przystosowy-
wanie istniejących gier do systemu ARENA sprowadza się do tworzenia odpowiednich kompo-
nentów adapterów; nowe gry, przeznaczone do umieszczenia w systemie ARENA, można od
początku tworzyć w sposób zgodny ze standardami tego systemu.
7.6. Analiza przypadku — system ARENA 337

Rysunek 7.12. Pierwszy fragment wyniku dekompozycji systemu ARENA — podsystemy odpowie-
dzialne za część organizacyjną

Instancje podsystemu TournamentManagement odwołują się do instancji podsystemu


GameManagement w celu inicjowania instancji podsystemu GamePeer i kolekcjonowania wyni-
ków poszczególnych meczów. Instancje podsystemu Match Front EndPeer odwołują się do
instancji podsystemu Adverti sementManagement w celu pobierania i wyświetlania banerów
reklamowych (patrz rysunek 7.13). Zauważmy, że dla gier turowych 10 architektura klient-
serwer byłaby wystarczająca, jako że czas reakcji gracza nie jest w tych grach czynnikiem
krytycznym. Architektura peer-to-peer nie stanowi jednak żadnej przeszkody dla gier two-
rzonych w stylu architektonicznym klient-serwer.

7.6.3. Odwzorowanie podsystemów w procesory i komponenty


Odwzorowywanie podsystemów w procesory i komponenty umożliwi zidentyfikowanie prze-
jawów współbieżnej pracy podsystemów, a także stanowić będzie krok w kierunku realizacji
celów projektowych dotyczących wydajności i niezawodności.
ARENA jest z założenia systemem rozproszonym — jego użytkownicy używają różnych
komputerów, często oddalonych od serwera o wiele stref czasowych. Mimo to, wyróżnimy je-
dynie dwa typy węzłów: UserMachi ne, realizujący interfejs użytkownika, oraz ServerMachi ne,

10
Patrz http://pl.wikipedia.org/wiki/Strategiczna_gra_turowa — przyp. tłum.
338 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Rysunek 7.13. Drugi fragment wyniku dekompozycji systemu ARENA — podsystemy odpowiedzialne
za rozgrywanie gier

realizujący logikę aplikacji, przechowywanie danych i tym podobne, czyli ogólnie udostępniają-
cy usługi systemu ARENA. Podsystemy ArenaCl ient i MatchFrontEndPeer zlokalizowane będą
bezdyskusyjnie na węźle UserMachi ne. Co się tyczy pozostałych podsystemów, to przy założeniu
niewielkiego obciążenia ze strony użytkowników mogłyby one być zlokalizowane na poje-
dynczym węźle ServerMachine; mając jednak na względzie postulat skalowalności systemu,
definiujemy kolejny podsystem Adverti sementServer dedykowany przesyłaniu banerów
reklamowych do przeglądarki i przypisujemy podsystemy Adverti sementServer, GamePeer,
ArenaStorage i ArenaServer do odrębnych procesów, z których każdy może być realizowany
na oddzielnym węźle ServerMachine. W komponencie ArenaServer zagnieżdżamy pod-
systemy TournamentManagement, UserManagement i GameManagement (patrz rysunek 7.14).

Rysunek 7.14. Odwzorowanie systemu ARENA w węzły sprzętowo-programowe; zauważmy, że każdy


k o m p o n e n t wykonawczy może zawierać kilka podsystemów
7.6. Analiza przypadku — system ARENA 339

Dla realizacji części organizacyjnej' systemu ARENA wybraliśmy środowisko Java EE. Sta-
nowi ono kolekcję interfejsów i standardów opracowanych przez firmę Sun Microsystems oraz
siłami wspólnoty entuzjastów, a umożliwia tworzenie wieloplatformowych systemów w języku
Java. Zaletą języka Java jest jego implementacja w szeregu produktów, zarówno komercyjnych,
jak i klasy open source, co ułatwia pogodzenie dwóch sprzecznych wielkości: skali (mierzonej
liczbą graczy, lig i turniejów) oraz kosztów (związanych między innymi z licencjonowaniem
komponentów). Ponadto komponenty klasy open source mają zwykle to do siebie, że łatwo się
je instaluje, a administrowanie nimi nie wymaga zaawansowanej wiedzy.
Podsystem ArenaCl ient fizycznie realizowany jest w postaci przeglądarki WWW, na-
tomiast ArenaServer i powiązane z nim podsystemy dostępne są za pośrednictwem serwera
WWW. Do realizacji tych podsystemów użyliśmy dwóch składników środowiska Java EE:
Java Servlets i Java Server Pages (JSP) jako głównej technologii implementowania obiektów
brzegowych. Klasy Java Servlets, zlokalizowane w węźle ServerMachi ne, przetwarzają żądania
otrzymywane z serwera WWW, produkując w odpowiedzi strony w języku HTML. JSP jest
natomiast zwięzłym narzędziem do programowania serwletów w języku zbliżonym do HTML,
który następnie przetwarzany jest przez preprocesor; wykorzystamy JSP do realizacji obiek-
tów brzegowych systemu ARENA. Obiekty brzegowe odwoływać się będą do metod obiektów
encji i obiektów sterujących, zrealizowanych także z użyciem klas Java Foundation.
Po zidentyfikowaniu podsystemów, współbieżności i odwzorowania sprzętowo-
programowego zajmijmy się teraz zarządzaniem obiektami trwałymi.

7.6.4. Identyfikowanie i przechowywanie trwałych danych

Identyfikowanie obiektów trwałych

W systemie ARENA występują dwa rodzaje obiektów o charakterze trwałym. Pierwszy


z nich obejmuje obiekty tworzone i wykorzystywane przez podsystemy organizacyjne — gra-
czy, gry, mecze i turnieje; obiekty te muszą być przechowywane również między kolejnymi
uruchomieniami systemu, by umożliwić śledzenie historii lig, meczów, turniejów i poszcze-
gólnych graczy. Obiekty drugiego rodzaju tworzone i wykorzystywane są przez podsystemy
GamePeer i MatchFrontEndPeer w związku z koniecznością odtwarzania zakończonych
meczów na żądanie kibiców, a także wznawiania meczów przerwanych w wyniku awarii
systemu. Pierwsza z wymienionych grup obiektów jest dobrze zdefiniowana i przypuszczalnie
nie będą się one znacząco zmieniać w okresie istnienia systemu. Z drugą grupą rzecz ma
się zgoła odmiennie: obiekty w niej są specyficzne dla poszczególnych gier i ich specyfika
spoczywa całkowicie w gestii twórców tych gier. Zdecydowaliśmy zatem o zarządzaniu obiek-
tami pierwszej grupy w ramach podsystemu ArenaStorage, twórcom gier pozostawiając
całkowicie kwestię zarządzania obiektami specyficznymi dla tych gier — obiekty te wyko-
rzystywane są jedynie przez interfejs konkretnej gry i nie mają bezpośredniego kontaktu
z resztą systemu.
340 Rozdział 7. • Projekt systemu: realizacja celów projektowych

Wybór strategii przechowywania obiektów trwałych

Zidentyfikowanie obiektów trwałych systemu umożliwia określenie strategii ich prze-


chowywania, z uwzględnieniem istotnych czynników tej strategii, czyli obsługi współbieżności
i odtwarzania danych po awarii systemu. Wiele systemów zarządzania danymi umożliwia ob-
sługę współbieżnych żądań, udostępniając system transakcji pozwalający na zachowywanie
integralności danych.
Jako że jednym z kluczowych celów projektowych systemu ARENA jest minimalizacja
kosztów, naturalne staje się rozważenie przechowywania wszelkich obiektów trwałych w for-
mie „płaskich" plików. Rozwiązanie takie nie wymaga żadnych zabiegów instalacyjnych czy
konfiguracyjnych i dostępne jest bez żadnych dodatkowych kosztów. System oparty całkowicie
na „płaskich" plikach nie jest jednak skalowalny, nie zdaje więc egzaminu w środowisku ob-
sługującym równocześnie dziesiątki gier i tysiące graczy. Aby zatem pogodzić niskie koszty
ze skalowalnością, wybieramy strategię mieszaną, opartą na połączeniu „płaskich" plików
z relacyjną bazą danych. Podsystem ArenaStorage udostępniać będzie abstrakcyjny interfejs
uwzględniający oba te rodzaje implementacji; przy instalowaniu systemu ARENA jego operator
(ArenaOperator) będzie miał możliwość wyboru strategii najbardziej odpowiadającej jego
potrzebom. Co prawda, operator ten nie będzie miał bezpośredniej możliwości samodzielnej
zmiany dokonanego wtedy wyboru, lecz zmianę taką umożliwią mu narzędzia rekonfiguracji,
dokonujące konwersji danych z „płaskich" plików do postaci relacyjnej bazy danych i od-
wrotnie. Rozwiązanie takie zwiększy koszty wytworzenia systemu ARENA, lecz zapewni opera-
torowi większą elastyczność.
Aby zredukować stopień ryzyka, pierwszy prototyp systemu oparty będzie w całości na
„płaskich" plikach, w drugim prototypie użyjemy natomiast interfejsu API relacyjnej bazy
danych, niezależnego jednak od konkretnej bazy, na przykład JDBC [JDBC, 2009], co pozwoli
na swobodny wybór tejże konkretnej bazy przez operatora.
Problem trwałego przechowywania obiektów drugiej grupy — obiektów specyficznych
dla poszczególnych gier — pozostawiamy całkowicie w gestii twórców gier. Biorąc pod uwagę
fakt, że gra jest czasową sekwencją zdarzeń, a więc dane odzwierciedlające jej historię będą miały
charakter sekwencyjny, przewidujemy wykorzystywanie w tej roli wyłącznie „płaskich" plików.

7.6.5. Definiowanie założeń kontroli dostępu


ARENA jest systemem wielodostępnym, różni aktorzy uprawnieni są do wykonywania różnych
operacji na różnych obiektach. Dla zwięzłego udokumentowania związanych z tym praw do-
stępu narysowaliśmy macierz dostępu, widoczną w tabeli 7.8, obrazującą uprawnienia dostępu
poszczególnych aktorów do konkretnych obiektów encji. W skrócie — operator Arena
"-•Operator może tworzyć obiekty reprezentujące użytkowników (User) i ligi (League), założy-
ciel ligi (LeagueOwner) może tworzyć obiekty reprezentujące turnieje (Tournament) i mecze
(Match), reklamodawcy (Adverti ser) mogą wysyłać i kasować reklamy, a także zgłaszać się do
sponsorowania lig i turniejów. Zauważmy, że założyciel ligi (LeagueOwner) podejmuje osta-
teczną decyzję na temat sponsoringu, zgodnie z zapisem w dokumencie RAD. Gracz (Player)
może subskrybować konkretną ligę (w celu otrzymywania ogłoszeń), zgłaszać się do udziału
w turniejach (Tournament) i rozgrywać mecze (Match) zaplanowane dla tej ligi. Wreszcie kibice
(Spectators) mogą oglądać statystyki graczy (Player), harmonogramy lig (League) i tur-
niejów (Tournament), subskrybować powiadomienia o meczach (Match) i je oglądać.
7.6. Analiza przypadku — system ARENA 341

Tabela 7.8. Macierz dostępu do obiektów systemu ARENA

Aktor/Obiekt Arena User League Tournament Match


ArenaOperator «create» «create» "createn
createUser deactivate archive
LeagueOwner getStats archive «create» «create»
getlnfo setSponsor archive kończenie
setSponsor

Advertiser uploadAds zgłoszenie zgłoszenie


removeAds do sponso- do sponso-
rowania rowania

PI ayer zgłoszenie setlnfo oglądanie, zgłoszenie rozgrywanie,


na założyciel subskrybo- udziału, oglą- kończenie
a ligi wanie danie, sub-
skrybowanie

Spectator zgłoszenie getStats oglądanie, oglądanie, subskrybowa-


gracza, zgło- subskrybo- subskrybo- nie, oglądanie,
szenie rekla- wanie wanie odtwarzanie
modawcy

Co prawda, większość informacji związanej z prawami dostępu wynika wprost z modelu


przypadków użycia, jednakże powyższa macierz stanowi ujęcie ich w zwięzłej i bardziej szcze-
gółowej formie. Ułatwia to klientowi jej weryfikację, a programistom poprawne implemen-
towanie.
Kibice (Spectator) nie wymagają logowania do systemu, wszyscy inni aktorzy muszą
zostać uwierzytelnieni, nim uzyskają możliwość modyfikowania obiektów systemu. Uwierzy-
telnienie to zrealizowaliśmy w formie standardowego dialogu „login/hasło", same zaś upraw-
nienia zaimplementowaliśmy w postaci list kontroli dostępu związanych z każdą z klas League,
Tournament i Match. Zalogowani aktualnie użytkownicy kontrolowani są poprzez dedykowane
im instancje klasy Session.

7.6.6. Projektowanie globalnego przepływu sterowania


Jak pisaliśmy w sekcji 7.4.4, programista ma do wyboru trzy paradygmaty przepływu ste-
rowania: proceduralny, zdarzeniowy i wielowątkowy. Wybór jednego z nich uzależniony jest
z jednej strony, wymaganiami dotyczącymi czasu reakcji i przepustowości, z drugiej zaś,
przewidywaną złożonością programowania. Co więcej, w systemie złożonym z wielu kompo-
nentów poszczególne komponenty wykorzystywać mogą różne paradygmaty sterowania.
Wybierając jednak w sekcjach 7.6.3 i 7.6.4 komponenty dla interfejsu użytkownika,
ograniczyliśmy sobie możliwości wyboru paradygmatu sterowania dla części organizacyjnej
systemu ARENA. Serwer W W W (Webserver) odbiera żądania z przeglądarki (WebBrowser)
i przetwarza je, kierując do odpowiedniego serwletu lub JSP — mamy więc do czynienia
z paradygmatem zdarzeniowym. Dla obsługi każdego zdarzenia Webserver tworzy nowy wą-
tek; niezależne realizowanie poszczególnych żądań zmniejsza czas reakcji systemu (serwer
może przyjmować nowe żądania, zanim jeszcze zakończy się realizacja uprzednio przyjętych)
342 Rozdział 7. • Projekt systemu: realizacja celów projektowych

oraz jego przepustowość (gdy jedno żądanie oczekuje na odpowiedź od podsystemu bazy
danych, inne mogą być przetwarzane przez procesor). Wybór paradygmatu wielowątkowego
skutkuje jednak większą złożonością systemu, wynikającą z konieczności synchronizowania
współbieżnych wątków. W systemie ARENA solidność rozwiązania w zakresie synchronizacji
przy dostępie do wspólnych danych zapewnić mają następujące założenia.

• Obiekty brzegowe nie powinny definiować żadnych pól. Obiekty brzegowe powinny
utrzymywać informację o stanie realizowanych żądań w lokalnych zmiennych, nie
w swych polach. Pozwoli to na uniknięcie zjawiska wyścigu („hazardu") w sytuacji,
gdy obiekt brzegowy współdzielony jest przez kilka wątków".
• Obiekty sterujące nie powinny być współdzielone przez wątki. Dla każdej sesji powi-
nien istnieć co najwyżej jeden obiekt sterujący, a użytkownicy nie powinni mieć
możliwości wysyłania równoległych żądań w tej samej sesji12. Reguła ta staje się
szczególnie istotna w odniesieniu do obiektów sterujących nieznikających po zakoń-
czeniu obsługi pojedynczego żądania.
• Obiekty encji nie powinny udostępniać bezpośrednio swoich pól. Wszelki dostęp do
tych pól musi odbywać się wyłącznie za pośrednictwem dedykowanych metod. Po-
nadto metody te powinny odwoływać się wyłącznie do pól tego obiektu, który zaini-
cjował ich wywołanie (identyfikowanego przez zmienną thi s), nie do pól w innych
instancjach tej samej klasy. W klasach zrealizowanych jako abstrakcyjne typy danych
(patrz sekcja 2.3.2) wszystkie pola powinny już być polami prywatnymi.
• Metody odczytujące lub modyfikujące stan obiektu powinny być synchronizowane.
Mechanizm synchronizacji dostarczany przez język Java zapewnia wówczas, iż dana
metoda wykonywana jest w danej chwili w ramach co najwyżej jednego wątku.
• Należy unikać zagnieżdżonych wywołań synchronizowanych metod, czyli wywoły-
wania jednej synchronizowanej metody w ciele innej synchronizowanej metody.
Takie zagnieżdżone wywołania stwarzają ryzyko wystąpienia zastoju (deadlock).
Gdy wywołań talach nie sposób uniknąć w obecnej postaci metod, programiści po-
winni dokonać tego poprzez ich rozdrobnienie albo przynajmniej wymusić określoną
kolejność wywoływania uzależnionych metod synchronizowanych 13 .
• Redundantne informacje o stanie obiektu powinny być opatrywane znacznikami
czasowymi. Gdy dwóch użytkowników przystępuje równocześnie do modyfikowania
stanu tego samego obiektu trwałego, informacja o tym stanie znajduje się w trzech
różnych miejscach: w bazie przechowującej ów obiekt i w formularzach obu użyt-
kowników. Te dwie ostatnie instancje informacji powinny być opatrzone znacznikiem
wskazującym chwilę ich wysłania, co ułatwi rozstrzygnięcie konfliktu między nimi.

11
Każdy wątek posiada wtedy własny, oddzielny zestaw zmiennych lokalnych, dzięki czemu po-
szczególne wątki nie interferują ze sobą; pola obiektu są natomiast wspólne dla wszystkich wątków
— przyp. tłum.
12
Czyli formułowania kolejnego żądania przed zakończeniem obsługi poprzedniego — przyp. tłum.
13
Wymuszenie określonej kolejności żądania zasobów przez równoległe wątki jest jedną ze standar-
dowych metod unikania zastojów — przyp. tłum.
7.6. Analiza przypadku — system ARENA 343

W części drugiej systemu ARENA,zarządzającej rozgrywaniem gier, podsystemy Match


"-•FrontEndPeer i GamePeer realizują swe funkcje w oddzielnych procesach, uruchamianych
przez podsystem GameManagement zgodnie z harmonogramem turniejów i uczestnictwem
graczy w rozgrywkach. Wewnętrzny przepływ sterowania w ramach każdego z komponentów
MatchFrontEndPeer i GamePeer może być realizowany zdarzeniowo lub wielowątkowo, zależ-
nie od specyfiki konkretnej gry.

7.6.7. Identyfikowanie usług


Po zidentyfikowaniu podsystemów i określeniu strategii przepływu sterowania w ramach
każdego komponentu (i systemu jako całości) czas na identyfikację usług oferowanych przez
poszczególne podsystemy.
W tym przykładzie skupimy się na zależnościach między podsystemami ArenaServer,
UserManagement, Adverti sementManagement i TournamentManagement w kontekście przy-
padku użycia OrganizeTournament (patrz tabela 4.13).
Zauważmy najpierw, że wszystkie żądania obsługiwane przez ArenaServer muszą być
autoryzowane zgodnie z założeniami kontroli dostępu określonymi w sekcji 7.6.5. Prowadzi
do stworzenia dwóch usług: Authentication, realizującej logowanie (uwierzytelnianie) użyt-
kownika i Authorization, weryfikującej, czy dane żądanie dopuszczalne jest dla roli, jaką
pełni aktualnie zalogowany użytkownik. Obie wymienione usługi przypisane zostają do pod-
systemu UserManagement.
W początkowych krokach przypadku użycia OrganizeTournament założyciel ligi
(LeagueOwner) organizuje nowy turniej (Tournament) w kontekście swojej ligi (League). Dla
podsystemu TournamentManagement potrzebne są więc usługi realizujące tę organizację oraz
udostępniające informacje na temat turniejów i lig. Usługi te, choć banalne, wymagane są przez
każdą z klas modelu analitycznego. Dla zaznaczenia, że podsystem TournamentManagement
obejmuje klasy Tournament i League, definiujemy usługi o tych właśnie nazwach. W praktyce
usługi tego rodzaju, jako oczywiste, nie są w sposób jawny specyfikowane w modelu, jako
że nie wnosiłyby praktycznie żadnej użytecznej informacji, za to powodowałyby dodatkową
jego komplikację.
W kolejnych krokach przypadku użycia OrganizeTournament założyciel ligi (League
"-•Owner) wystosowuje do reklamodawców (Advertiser) zaproszenia do sponsorowania no-
wego turnieju. Dodajemy zatem do podsystemu Adverti sementManagement usługę Sponsorshi p,
umożliwiającą wysyłanie wspomnianych zaproszeń i udzielanie na nie odpowiedzi przez za-
interesowanych sponsorów. Łącząc w ramach jednej usługi dwie różne akcje, utrzymujemy
w jednym miejscu wszystkie operacje powiązane ze sponsoringiem.
W związku z wyborem reklamodawcy sponsora (Avdertiser) przez założyciela ligi
(LeagueOwner) stajemy przed koniecznością zdefiniowania kolejnej usługi, którą przypo-
rządkować można by zarówno do podsystemu Adverti sementManagement, jak i podsystemu
TournamentManagement, ze względu na łączące te podsystemy skojarzenie między klasami
Tournament i Advertiser. Jako że treść przypadku użycia OrganizeTournament koncentruje
się wokół definicji turnieju, decydujemy się powierzyć podsystemowi TournamentManagement
śledzenie stanu tego przypadku. W kategoriach modelu analitycznego podsystem ten jest
właścicielem obiektu sterującego związanego ze wspomnianym przypadkiem. Ostatecznie więc
przedmiotowa usługa — nazwana SponsorSel ection — ląduje w podsystemie Tournament
"^-Management.
344 Rozdział 7. • Projekt systemu: realizacja celów projektowych

W kolejnych krokach przypadku użycia OrganizeTournament założyciel ligi (League


>-*0wner) ogłasza zorganizowanie nowego turnieju, gracze zgłaszają się do udziału w nim, nie-
które ze zgłoszeń zostają przyjęte, a my stajemy przed kolejnym problemem przyporządko-
wania stosownej usługi Player do podsystemu UserManagement albo TournamentManagement.
Jako że gracz (Player) jest ściśle powiązany ze swą ligą i turniejem, decydujemy się na wybór
podsystemu TournamentManagement. Ponieważ przyjęcie gracza do udziału w turnieju daleko
wykracza poza utworzenie obiektu PI ayer, definiujemy nową usługę PI ayerAcceptance reali-
zującą ten krok.
Zidentyfikowane do tej pory usługi przedstawione są na diagramie z rysunku 7.15. Ana-
lizując powyższą dyskusję, można zauważyć dwie następujące tendencje:

• Decydując się na wybór jednego z dwóch podsystemów jako docelowego dla nowej
usługi, staramy się koncentrować funkcjonalność w podsystemie, w którym definio-
wany jest obiekt sterujący odnośnego przypadku użycia. Z jednej strony, prowadzi
to do dużej spoistości podsystemu, z drugiej jednak, stwarza ryzyko jego nadmiernej
komplikacji.
• Definiowanie usług na bazie poszczególnych kroków przypadku użycia prowadzi do
drobnoziarnistych usług. Mimo iż drobnoziarnistość ta może być znakomitym spraw-
dzianem dla dokonanej dekompozycji, to jednak jej konsekwencją jest duża liczba
interfejsów dedykowanych pojedynczym operacjom. To znak, że zbyt szybko zbli-
żamy się do etapu projektowania obiektów. Należy wówczas zastanowić się nad
konsolidacją powiązanych ze sobą usług w pojedyncze usługi, co pozwoli zachować
stopień abstrakcji właściwy dla poziomu architektury systemu i jednocześnie zapo-
biegnie utracie czytelności modelu. Staranne wybieranie dla usług odpowiednich
nazw, w postaci rzeczowników oznaczających kolekcje operacji, może stanowić
czynnik przeciwdziałający wspomnianemu rozdrobnieniu.

Rysunek 7.15. Część organizacyjna dekompozycji systemu ARENA, widoczne są zidentyfikowane usługi.
Dla przejrzystości zrezygnowaliśmy z uwidocznienia zależności między podsystemami
7.6. Analiza przypadku — system ARENA 345

7.6.8. Identyfikowanie warunków granicznych


Teraz na podstawie podjętych dotąd decyzji projektowych zidentyfikujemy graniczne przy-
padki użycia. Na początek rozważymy czas życia obiektów trwałych systemu ARENA, czas
życia każdego komponentu wykonawczego i możliwe typy awarii systemu.

Konfiguracyjne przypadki użycia

Zarządzanie obiektami trwałymi jest już w większości opisane w przypadkach użycia


opracowanych na etapie analizy wymagań (patrz rysunek 4.9 i tabela 4.11) i w macierzy dostępu
(patrz tabela 7.8). I tak na przykład operator ArenaOperator definiuje nowych użytkowni-
ków (User) w systemie i usuwa z systemu ich profile, LeagueOwner tworzy ligi (League) i tur-
nieje (Tournament), gracze (Player) rozpoczynają i kończą mecze, reklamodawcy (Advertiser)
zarządzają swymi banerami (Adverti sementBanner). Jak dotąd jednak w żadnym z przy-
padków użycia nie zostało opisane zarządzanie obiektami Arena i Game. Pierwszy z nich repre-
zentuje instancję systemu ARENA i tworzony jest przy jego instalowaniu, drugi reprezentuje
konkretną grę, tworzony jest w momencie wprowadzania tej gry do systemu i usuwany
z systemu wraz z ową grą. Wynika stąd konieczność zdefiniowania dwóch dodatkowych
przypadków użycia, inicjowanych przez aktora, takich jak ArenaOperator: Instal 1 Arena
i ManageGames. Ponadto, jak wspomnieliśmy w sekcji 7.6.4, operator ma możliwość konwersji
danych systemu z postaci „płaskich" plików do postaci relacyjnej bazy danych i odwrotnie, co
odzwierciedlone zostaje w kolejnym przypadku użycia ConvertPersi stentStorage. Krótki opis
trzech nowych przypadków użycia zamieszczamy w tabeli 7.9.

Tabela 7.9. Dodatkowe, graniczne przypadki użycia związane z obiektami trwałymi systemu ARENA

InstallArena A r e n a O p e r a t o r tworzy instancję systemu, r e p r e z e n t o w a n ą


przez obiekt Arena, nadaje m u nazwę, decyduje o strategii jego
przechowywania („płaskie" pliki albo relacyjna baza danych)
i konfiguruje jego parametry dotyczące zasobów (między innymi
maksymalną liczbę jednoczesnych turniejów, ścieżkę określającą
lokalizację pamięci dla obiektów trwałych).
ManageGames ArenaOperator instaluje lub usuwa grę (Game), wraz z kodem
źródłowym specyficznych dla gry k o m p o n e n t ó w GamePeer
i MatchFrontEndPeer. Lista gier zostaje zaktualizowana, kolejny
założyciel ligi (LeagueOwner) organizujący nową ligę (League)
zobaczy ją w aktualnej postaci.
ConvertPersi s t e n t S t o r a g e ArenaOperator po zamknięciu serwera ArenaServer ma możliwość
konwersji danych trwałych z postaci „płaskich" plików na postać
relacyjnej bazy danych i odwrotnie.

Przypadki użycia związane z rozpoczynaniem i kończeniem pracy systemu

Jak to widać na diagramie wdrażania z rysunku 7.14, system ARENA zawiera pięć
komponentów wykonawczych: WebBrowser, ArenaServer (zawierający pięć podsystemów
— UserManagement, GameManagement, TournamentManagement, Notification i ArenaStorage),
346 Rozdział 7. • Projekt systemu: realizacja celów projektowych

MatchFrontEndPeer, GamePeer i (w drugiej wersji prototypu) DatabaseServer. Komponenty


WebBrowser i DatabaseServer są zamkniętymi całościami „z półki", uruchamianymi i zamy-
kanymi niezależnie. Podsystemy MatchFrontEndPeer i GamePeer uruchamiane są i zamykane
przez (odpowiednio) WebBrowser i ArenaServer. Uruchamianie i zamykanie komponentu
ArenaServer nie zostało jeszcze opisane — brak ten uzupełniają dwa kolejne przypadki użycia
wymienione w tabeli 7.10.

Tabela 7.10. Dodatkowe, graniczne przypadki użycia związane z k o m p o n e n t a m i wykonawczymi


systemu ARENA

StartArenaServer ArenaOperator uruchamia ArenaServer. Jeśli poprzednia praca serwera


nie zakończyła się poprawnym zamknięciem, wywoływany jest przypadek
użycia CheckDatalntegri ty, opisany w następnej sekcji. Gdy zakończy
się inicjowanie pracy serwera k o m p o n e n t u ArenaServer, założyciele lig
(LeagueOwner), gracze (PI ayer), kibice ( S p e c t a t o r ) i reklamodawcy
(Adverti s e r ) mogą inicjować „swoje" przypadki użycia.

ShutDownArenaServer ArenaOperator zatrzymuje ArenaServer. Kończone są ewentualne trwające


mecze (Match), zapisywane są wszelkie dane obecne w pamięci cache systemu.
Z a m y k a n e są wszystkie instancje p o d s y s t e m ó w MatchFrontEndPeer
i GamePeer. Po zakończeniu tego p r z y p a d k u użycia żaden z a k t o r ó w
LeagueOwner, PI a y e r , S p e c t a t o r i A d v e r t i s e r nie m o ż e uzyskać
jakiegokolwiek dostępu do obiektu Arena.

Przypadki użycia dla sytuacji wyjątkowych

System ARENA może w swej pracy doświadczyć awarii dających się sklasyfikować w ra-
mach czterech następujących kategorii:

• awaria sieci powodująca zerwanie jednego lub wielu połączeń między instancjami
podsystemów MatchFrontEndPeer i GamePeer,
• awaria hosta lub komponentu, wskutek której nieoczekiwanie przerwana zostaje
praca jednej lub wielu instancji podsystemu MatchFrontEndPeer lub GamePeer,
• awaria sieci powodująca zerwanie jednego lub wielu połączeń między instancja-
mi podsystemów WebBrowser i ArenaServer,
• awaria serwera powodująca nagłe przerwanie pracy komponentu ArenaServer.

Obsługę dwóch pierwszych klas zdecydowaliśmy się powierzyć obiektom Game repre-
zentującym konkretne gry. Co prawda, wyposażymy komponenty MatchFrontEndPeer
i GamePeer w generyczne metody przywracania zerwanych połączeń i przywracania stanu
przerwanych meczów, jednakże szczegółowa reakcja na zerwanie połączenia zależna jest
od specyfiki konkretnej gry: gry symulacyjne i gry rozgrywane w czasie rzeczywistym muszą
zostać zakończone lub ponownie rozpoczęte, podczas gdy w przypadku gier planszowych
krótkotrwałe utraty połączeń mogą być nawet niezauważalne dla graczy. Pozostawiliśmy
zatem twórcom gier swobodę w zakresie postępowania z awariami tego typu.
7.6. Analiza przypadku — system ARENA 347

Zerwanie połączenia przeglądarki'WWW z serwerem obsługiwane jest standardowo


przez wszystkie popularne przeglądarki: użytkownik zostaje poinformowany o zaistniałej sy-
tuacji i możliwości ponowienia wykonywanej operacji, niestety, dane wprowadzone do for-
mularza mogą przepaść bezpowrotnie. Wynika stąd ważne zalecenie takiego projektowania
formularzy, by objętość danych traconych przy takiej okazji była stosunkowo niewielka.
Obsługę awarii ostatniego z wymienionych typów opisuje przypadek użycia CheckData
^ I n t e g r i t y (patrz tabela 7.11), odzwierciedlający weryfikację integralności danych po nie-
oczekiwanym przerwaniu pracy serwera ArenaServer. Przypadek ten może być uruchamiany
automatycznie przy starcie systemu (patrz przypadek użycia StartArenaServer w tabeli 7.10),
może go także zainicjować operator ArenaOperator. Przy okazji identyfikujemy jeszcze jeden
przypadek użycia, opisujący restart przerwanej pracy instancji podsystemu GamePeer i sto-
sowne powiadomienie zainteresowanych graczy.

Tabela 7.11. Dodatkowe, graniczne przypadki użycia związane z awariami systemu ARENA

CheckDatalntegrity System ARENA d o k o n u j e zweryfikowania integralności trwałych


danych. Dla reprezentacji „płaskich" plików może to ograniczać się
do sprawdzenia, czy ostatnio zapoczątkowane transakcje zostały
utrwalone na dysku. Dla relacyjnej bazy d a n y c h weryfikacja taka
może oznaczać wywołanie specjalnych narzędzi d o k o n u j ą c y c h
na przykład ponownego utworzenia indeksów tabel.
RestartGamePeers System ARENA u r u c h a m i a p r z e r w a n e mecze (Match) i powiadamia
wszystkie aktywne instancje p o d s y s t e m u MatchFrontEndPeer
o wznowieniu pracy odnośnych instancji podsystemu GamePeer.

7.6.9. Wnioski
W zakończonej właśnie sekcji przeanalizowaliśmy problemy związane z projektem systemu
ARENA. Zidentyfikowaliśmy cele projektowe i nadaliśmy im priorytety, dokonaliśmy de-
kompozycji systemu, odwzorowaliśmy poszczególne podsystemy w komponenty i platformę
sprzętowo-programową, wybraliśmy strategię przechowywania trwałych danych, zdefiniowa-
liśmy założenia kontroli dostępu do obiektów systemu, dostarczyliśmy rozwiązania najważ-
niejszych problemów związanych ze współbieżnością, wreszcie skonstruowaliśmy przypadki
użycia odzwierciedlające obsługę sytuacji granicznych i wyjątkowych. Na podstawie przepro-
wadzonej analizy mogliśmy się przekonać, że:

• większość problemów towarzyszących projektowaniu systemu jest ze sobą powiązana;


przykładowo wybór konkretnego komponentu dla realizacji obiektów brzegowych
lub wybór odpowiedniej strategii przechowywania danych ma związek z ogólną stra-
tegią przepływu sterowania w systemie,
• rozwiązania niektórych problemów projektowych mogą być zróżnicowane, zależnie
od tego, do której części systemu obiekty te się odnoszą; przykładowo problemy
związane z architekturą, przepływem sterowania, odtwarzaniem danych po awarii
czy przechowywaniem danych rozwiązuje się inaczej w części organizacyjnej systemu
ARENA, a inaczej w części zarządzającej przeprowadzaniem gier,
348 Rozdział 7. • Projekt systemu: realizacja celów projektowych

• niektóre problemy projektowe można pozostawić otwarte, aż do fazy projektowania


obiektów (do takich problemów należą między innymi decyzje związane z podsys-
temem GamePeer lub nawet z późniejszymi emisjami systemu (takie jak wybór rela-
cyjnej bazy danych, istotny dopiero w drugiej wersji prototypu),
• formułując cele projektowe, należy dla każdego z nich określać istotność w formie
przypisywania priorytetu i rozważać różne możliwości projektowe.

7.7. Literatura uzupełniająca


Rozstrzyganie konfliktów między celami projektowymi jest trudnym zadaniem, które może
być wykonywane jedynie metodą prób i błędów, i które wymaga praktyki i doświadczenia.
Niewielka jest więc liczba publikacji poświeconych tej tematyce: książki L. Bassa, P. Clementsa
i R. Kazmana [Bass i in., 2003] oraz P. Clementsa, R. Kazmana i M. Kleina [Clements i in.,
2002] koncentrują się na ogólnych metodach oceny architektury, sformułowanych w postaci
zbioru celów projektowych. Obie książki dostarczają także licznych analiz przypadku, ilu-
strujących ówczesny stan wiedzy w tej dziedzinie.
W książce M. Błahy i W Premarlaniego [Błaha i Premerlani, 1998] opisane są metody
tworzenia aplikacji bazodanowych, ze szczególnym uwzględnieniem wydajności, rozszerzal-
ności i modyfikowalności.
Książka D. P. Siewiorka i R. S. Swarza [Siewiorek i Swarz, 1992], traktująca o projekto-
waniu niezawodnych systemów, zawiera bogaty przegląd technik i metod osiągania rzeczonej
niezawodności, jak również pokaźną liczbę analiz przypadku zaczerpniętych z projektów
o charakterze przemysłowym.
Zamieszczone w książce N. G. Leveson [Leveson, 1995] liczne przykłady awarii syste-
mów komputerowych i ich konsekwencji skłaniają do wielu ciekawych konkluzji. Książka
zawiera także przegląd ówczesnych metod podejścia do problemu bezpieczeństwa i eksponuje
potrzebę wszechstronnych metodologii związanych z projektowaniem systemów.
Wprowadzenie do projektowania aplikacji webowych zorientowanych na usługi (SOA
— Service Oriented Architecture) oraz przewodnik po tej tematyce to treść książki T. Erla
[Erl, 2005]. Celem architektury SOA jest strukturalizacja aplikacji pod kątem procesów bizne-
sowych oraz łatwość jej rozbudowy, dzięki elastyczności kojarzenia usług udostępnianych
użytkownikom.

7.8. Ćwiczenia
7.1. Rozpatrzmy system złożony z serwera W W W i dwóch serwerów bazodanowych.
Oba serwery bazodanowe są identyczne: pierwszy funkcjonuje jako główny serwer,
drugi — jako redundantny serwer zapasowy, włączany w przypadku awarii pierw-
szego. Użytkownicy wykorzystują przeglądarkę W W W w celu dostępu do danych,
za pośrednictwem — oczywiście — serwera WWW, lecz mają także do dyspozycji
dedykowane narzędzie, umożliwiające dostęp do serwera bazodanowego w sposób
bezpośredni. Narysuj diagram wdrażania UML przedstawiający odwzorowanie tego
systemu w węzły sprzętowo-programowe.
Bibliografia 349

7.2. Producent samolotów posługuje się przestarzałym, opartym na faksie systemem ra-
portowania błędów. Ty uczestniczysz w projekcie zastąpienia tego systemu syste-
mem komputerowym, obejmującym bazę danych i podsystem powiadamiania. Klient
wymaga, by faks nadal pozostał urządzeniem wejściowym, Ty proponujesz e-mail
jako narzędzie wprowadzania problemów do systemu. Opisz dekompozycję systemu
uwzględniającą interfejsy dla obu tych rozwiązań. Weź pod uwagę fakt, że inten-
sywność raportowania problemów może być bardzo duża (2000 faksów dziennie).
7.3. Wyobraź sobie, że projektujesz założenia kontroli dostępu dla systemu realizującego
sklep internetowy. Klienci, kontaktujący się ze sklepem za pośrednictwem przeglą-
darki WWW, mogą przeglądać ofertę towarów, wprowadzać swoje dane osobowe
i informacje na temat płatności, i — oczywiście — kupować towary. Dostawcy mogą
rejestrować nowe produkty, modyfikować informacje o swoich produktach i przyj-
mować zamówienia. Właściciel sklepu ustala ceny detaliczne, formułuje zróżnico-
wane oferty dla poszczególnych klientów, bazując na ich profilach, i świadczy usługi
marketingowe. W systemie tym należy uwzględnić trzech aktorów: administratora
(StoreAdmi ni s t r a t o r ) , dostawcę (Supplier) i klienta (Customer). Zaprojektuj listy
uprawnień dla każdego z wymienionych aktorów. Nowi klienci mogą sami rejestro-
wać się za pośrednictwem WWW, lecz nowych dostawców rejestrować może tylko
administrator.
7.4. Zaproponuj najbardziej odpowiedni mechanizm przepływu sterowania dla każdego
z wymienionych poniżej podsystemów. Ponieważ w większości przypadków wybór
nie jest oczywisty, uzasadnij swoje preferencje.
® Serwer W W W zdolny wytrzymać długotrwałe duże obciążenie.
® Interfejs GUI procesora tekstów.
• Osadzony (wbudowany) system czasu rzeczywistego, na przykład system ste-
rowania startem satelity.
7.5. Dlaczego graniczne przypadki użycia nie są konstruowane na etapie zbierania wy-
magań ani na etapie ich analizy?
7.6. Projektujesz właśnie podsystem cache'owania informacji pobieranych z internetu
(lub innej sieci) na szybszym nośniku, na przykład dysku twardym. W związku ze
zmianami w wymaganiach zamierzasz wyposażyć swój podsystem w nową usługę
konfiguracyjną, umożliwiającą ustalanie parametrów cache'owania (na przykład
maksymalnej wielkości przestrzeni dyskowej, jaką można zużyć na potrzeby tego
cache'owania). Których uczestników projektu powinieneś powiadomić o tej zmianie?

Bibliografia
[Bass i in., 2003] L. Bass, P. Clements, R. Kazman Software Architecture in Practice,
wyd. drugie, Addison-Wesley, Reading, MA, 2003.

[Błaha i Premerlani, 1998] M. Błaha, W. Premerlani Object-Oriented Modeling and Design for
Database Applications, Prentice Hall, Upper Saddle River, NJ, 1998.

[Clements i in., 2002] P. Clements, R. Kazman, M. Klein Evaluating Software Architectures:


Methods and Case Studies, SEI Series in Software Engineering,
Addison-Wesley, Reading, MA, 2002.
350 Rozdział 7. • Projekt systemu: realizacja celów projektowych

[Erl, 2005] T. Erl Service-Oriented Architectures:Concepts, Technology, and Design,


Prentice Hall, Upper Saddle River, NJ, 2005.
[Gamma i in., 1994] E. G a m m a , R. H e l m , R. Johnson, J. Vlissides Design Patterns:
Elements of Reusable Object-Oriented Software, Addison-Wesley,
Reading, MA, 1994. Wydanie polskie Wzorce projektowe. Elementy
oprogramowania obiektowego wielokrotnego użytku, Helion 2010.

[Hofmeister, 2000] C. Hofmeister, R. Nord, D. Soni Applied Software Architecture,


Object Technology Series, Addison-Wesley, Reading, MA, 2000.
[Jacobson i in., 1999] I. Jacobson, G. Booch, J. Rumbaugh The Unified Software
Development Process, Addison-Wesley, Reading, MA, 1999.
[JDBC, 2009] JDBC™ — Connecting Java and Databases, JDK D o c u m e n t a t i o n ,
Javasoft, 2009.
[Leveson, 1995] N. G. Leveson Safeware: System Safety And Computers, Addison-Wesley,
Reading, MA, 1995.
[RMI, 2009] Java Remote Method Invocation, JDK Documentation. Javasoft, 2009.

[Siewiorek & Swarz, 1992] D. P. Siewiorek, R. S. Swarz Reliable Computer Systems: Design
and Evaluation, wyd. drugie, Digital, Burlington, MA, 1992.
8.1. Wstęp: wpadki produkcyjne 354

8.2. O projektowaniu obiektów ogólnie 355

8.3. Koncepcja wielokrotnego wykorzystywania 359

8.3. Koncepcja wielokrotnego wykorzystywania


— dziedziczenie, delegowanie i wzorce projektowe 359

8.3. Koncepcja wielokrotnego wykorzystywania 359


8.3.1. Obiekty aplikacyjne i obiekty realizacyjne 359
8.3.2. Dziedziczenie implementacyjne i dziedziczenie specyfikacyjne 360
8.3.3. Delegowanie 363
8.3.4. Zasada zastępowania Liskov 364
8.3.5. Delegowanie i dziedziczenie we wzorcach projektowych 364

8.4. Wybór wzorców projektowych i gotowych komponentów 367


8.4.1. Hermetyzacja przechowywania danych
za pomocą wzorca projektowego Most 368
8.4.2. Hermetyzacja niekompatybilnych komponentów
za pomocą wzorca projektowego Adapter 371
8.4.3. Hermetyzacja kontekstu za pomocą
wzorca projektowego Strategia 373
8.4.4. Hermetyzacja platformy za pomocą
wzorca projektowego Fabryka abstrakcyjna 376
8.4.5. Hermetyzacja przepływu sterowania
za pomocą wzorca projektowego Polecenie 377
8.4.6. Hermetyzacja hierarchii za pomocą
wzorca projektowego Kompozyt 378
8.4.7. Heurystyki wyboru wzorców projektowych 379
8.4.8. Identyfikowanie i przystosowywanie
frameworków aplikacyjnych 381

8.5. Zarządzanie wykorzystywaniem gotowych rozwiązań 386


8.5.1. Dokumentowanie wykorzystywania gotowych rozwiązań 388
8.5.2. Przydzielanie odpowiedzialności 389

8.6. Analiza przypadku — system ARENA 390


8.6.1. Zastosowanie wzorca projektowego Fabryka abstrakcyjna 390
8.6.2. Zastosowanie wzorca projektowego Polecenie 392
8.6.3. Zastosowanie wzorca projektowego Obserwator 393
8.6.4. Wnioski 393

8.7. Literatura uzupełniająca 394

8.8. Ćwiczenia 395

Bibliografia 396
Projektowanie obiektów:
wielokrotne
wykorzystywanie
rozwiązań wzorcowych
Oszukujesz, jeśli nie doceniasz wkładu innych.
— Uniwersytet Carnegie Mellon, kurs 15-413
„Introduction to Software Engineering"
(http:// www. cs. cm u.edul-ałdrichlcourses!413!)

I ^ o d c z a s analizy wymagań opisujemy cel powstania systemu — rezultatem tego są obiekty


dziedziny aplikacyjnej. Na etapie projektowania systemu opisujemy ten system w kategoriach
jego architektury: dekompozycji na podsystemy, przepływu sterowania i zarządzania trwałymi
danymi; definiujemy wówczas także sprzętową i programową charakterystykę platformy, na
której eksploatowany będzie powstający system. Pozwala to na wybór gotowych komponen-
tów dostarczających wyższy poziom abstrakcji niż „goły" sprzęt. Kolejny etap — projektowa-
nie obiektów — ma na celu wypełnienie luki między obiektami dziedziny aplikacyjnej a kom-
ponentami wybranymi na etapie projektowania systemu; dokonuje się tego zarówno poprzez
identyfikowanie nowych obiektów z dziedziny realizacyjnej, jak i przez doskonalenie obiek-
tów już istniejących. Głównymi elementami procesu projektowania obiektów są:

• wykorzystywanie gotowych rozwiązań, którymi są i gotowe produkty (kompo-


nenty), i wzorce projektowe;
• specyfikowanie usług, czyli precyzyjne opisywanie wszystkich interfejsów;
• restrukturyzacja modelu obiektowego, w ramach której model ten jest prze-
kształcany w celu przydania mu większej elastyczności (rozszerzalności) i lepszej
czytelności;
• optymalizacja modelu obiektowego, w ramach której model ten przekształcany
jest pod kątem lepszej wydajności, czyli między innymi krótszego czasu reakcji i mniej-
szego zużycia pamięci.

Projektowanie obiektów, podobnie jak projektowanie systemu, nie jest procesem algo-
rytmicznym: sednem rozwiązania problemu staje się właściwa identyfikacja dostępnych wzor-
ców i komponentów — związane z tym aktywności są właśnie treścią tego i dwóch następ-
nych rozdziałów. W tym rozdziale skupimy się na wykorzystywaniu gotowych rozwiązań,
w następnym zajmiemy się specyfikowaniem usług; w rozdziale 10. omówimy zabiegi doty-
czące optymalizacji modelu.
354 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

8.1. Wstęp: wpadki produkcyjne


Żeby było ciekawiej — kilka epizodów ze świata filmu.

Speed: Niebezpieczna prędkość (1994)


Harry, policjant z Los Angeles, zostaje wzięty jako zakładnik przez Howarda, szalonego piro-
technika. Jack, partner Harry ego, strzela i rani go w nogę, by opóźnić ucieczkę Howarda. Harry
postrzelony został w lewą nogę, tymczasem przez cały film utyka na prawą. Budżet filmu:
30 milionów dolarów.

Trylogia Gwiezdne Wojny (1977, 1980, 1983)


Pod koniec części V Imperium kontratakuje (1980) Han Solo zostaje pojmany i zamrożony w celu
dostarczenia do Jabby. Na początku części VI Powrót Jedi (1983) zamrożony Han Solo zostaje odbity
przez swych przyjaciół, odmrożony i przywrócony do życia. Wsadzany do zamrażarki Han Solo ma
na sobie kurtkę; gdy przyjaciele go reanimują, ma na sobie białą koszulkę. Budżet obu filmów:
18 milionów i 32,5 milionów dolarów.

Titanic (1997)
Jack, prosty rybak, uczy Rose, damę z wyższych sfer, trudnej sztuki plucia. Pokazuje jej tajniki tej
sztuki i zachęca do praktykowania, gdy nagle zjawia się matka Rose. Gdy Jack zaczyna odwracać się
do niej, ma twarz całkiem czystą; za chwilę jednak, gdy zwrócony jest w stronę matki Rose, widzimy
ślady oplucia na jego podbródku. Budżet filmu: 200 milionów dolarów.

Podobne wpadki zdarzają się w wielu filmach, co — zważywszy na budżet ich realizacji
i na banalny w gruncie rzeczy charakter wspomnianych niedopatrzeń — może wydawać się
cokolwiek zdumiewające. Produkcja filmu jest jednak przedsięwzięciem skomplikowanym
znacznie bardziej, niż mogłoby się to wydawać w pierwszej chwili.
Przysłowiowe licho, które nie śpi i tylko podstępnie czyha na biednych realizatorów, kon-
spiruje ukryte pod wieloma czynnikami. Przy kręceniu i produkcji filmu współpracuje mnó-
stwo ludzi, sceny kręcone są zwykle w kolejności innej od tej, w jakiej następują w scenariuszu,
niektóre „dokręcane" są poza harmonogramem; szczegóły — kostiumy, rekwizyty — zmie-
niają się w czasie zdjęć. Po nakręceniu scen, gdy film jest edytowany i montowany, występuje
silna presja na ukończenie prac w założonym terminie. Kolejne sceny muszą być nagrywane
w sposób spójny — stan każdej sceny musi być zgodny ze stanem scen poprzedzających ją
i następujących po niej. Ów „stan" obejmuje mnóstwo drobiazgów: od ubioru aktora, jego fry-
zury, makijażu, biżuterii, aż po elementy otoczenia odbijające się w szkłach jego okularów. Gdy
w którejś ze scen następuje jakaś istotna zmiana, na przykład pojawienie się lub zniknięcie
rekwizytu, nie może ona pozostawać w sprzeczności z żadną z pozostałych scen. Gdy poje-
dyncze sceny integrowane są ze sobą, nad zachowaniem opisywanej spójności czuwa osoba
zwana „redaktorem ciągłości" (continuity editor).
Nie bez powodu nawiązujemy tu do świata filmu, bowiem tworzenie systemu informa-
tycznego ma w sobie coś z kręcenia i montowania filmu: jest skomplikowane, podatne na
zmiany i odbywa się pod presją harmonogramu. Etap projektowania obiektów przypomina
jako żywo dokręcanie brakujących scen: jego istotą jest wypełnianie luki, jaka istnieje między
obiektami dziedziny aplikacyjnej, zidentyfikowanymi na etapie analizy wymagań, a sprzętowo-
-programową platformą wykonawczą, jaka określona została na etapie projektowania systemu.
8.2. O projektowaniu obiektów ogólnie 355

Programiści realizują to zadanie, konstruując niezbędne obiekty w warunkach iście hollywo-


odzkiego rozproszenia: poszczególne obiekty tworzone są niezależnie od siebie, przez róż-
nych programistów i doznają licznych przeobrażeń, zanim przybiorą stabilny kształt. Często
bywa tak, że programista tworzący obiekt, a korzystający z funkcji innego obiektu, ma o tych
funkcjach tylko ogólne pojęcie, a często polega na domyślnych założeniach dotyczących efektów
ubocznych tych funkcji, które to założenia niekoniecznie muszą być prawdziwe. Aby uniknąć
nieporozumień tego typu, skutkujących błędną realizacją niektórych elementów funkcjonal-
nych (lub wręcz brakiem tej realizacji), programiści konstruują szczegółową specyfikację klas,
atrybutów i operacji, stosownie do zdefiniowanych warunków i ograniczeń. Ponieważ nie-
celowe jest ponowne wynajdywanie koła ani ponowne odkrywanie Ameryki, programi-
ści wykorzystują dostępny repertuar gotowych rozwiązań, przystosowując odpowiednio do
swych potrzeb komponenty „z półki". Tym wszystkim zabiegom towarzyszy optymalizowa-
nie modelu projektowania obiektów, talc by uczynić zadość sformułowanym celom projektowym
dotyczącym efektywności, rozszerzalności, pielęgnowalności i reaktywności systemu; nie wolno
przy tym zapomnieć o terminowym dostarczeniu systemu zgodnie z harmonogramem.
Ogólny opis fazy projektowania obiektów przedstawiamy w sekcji 8.2. W sekcji 8.3
definiujemy podstawowe koncepcje dotyczące tej fazy, między innymi ograniczenia wyko-
rzystywane do specyfikowania interfejsów. Sekcję 8.4 poświęcamy szczegółowemu opisowi
aktywności związanych z projektowaniem obiektów, zaś sekcję 8.5 — problemom menedżer-
skim związanym z projektowaniem obiektów. Zrezygnowaliśmy z opisywania aktywności,
takich jak implementowanie algorytmów i struktur danych czy też wykorzystywanie określo-
nych języków programowania, z dwóch powodów: po pierwsze, zakładamy, że czytelnicy mają
pewne doświadczenie w tym temacie, po drugie, wobec coraz większej dostępności gotowych,
sprawdzonych rozwiązań i komponentów zagadnienia te nie mają dziś krytycznego znaczenia
dla realizacji projektów informatycznych.

8.2. O projektowaniu obiektów ogólnie


Koncepcyjnie rzecz biorąc, cały sens tworzenia systemu informatycznego sprowadza się do
zasypywania przepaści istniejącej między problemem, którego rozwiązania oczekujemy od sze-
roko pojętej informatyki, a komputerem (komputerami), który ma to rozwiązanie fizycznie
wyprodukować. Zasypywanie to odbywa się w sposób przyrostowy, zilustrowany na stylizo-
wanym diagramie widocznym na rysunku 8.1.1 tale na etapie analizy wymagań system opisany
zostaje w kategoriach zewnętrznych przejawów swego zachowania — czyli w kategoriach
funkcjonalnych (określonych przez model przypadków użycia), kategoriach koncepcji dzie-
dziny aplikacyjnej, podlegających przetwarzaniu przez system (kategorie te reprezentowane
są przez obiekty modelu analitycznego) — zachowania postrzeganego jako przebiegające
w czasie interakcje (będące treścią modelu dynamicznego) i wymagania pozafunkcyjne.
Z kolei etap projektowania systemu przyczynia się do zmniejszania wspomnianej prze-
paści na dwa sposoby. Po pierwsze, zdefiniowana na tym etapie maszyna wirtualna stanowi
wyższy poziom abstrakcji niż „goły" komputer. Tę wirtualną maszynę tworzą różne gotowe
komponenty: oprogramowanie pośrednie (middleware), narzędzia do realizacji interfejsu użyt-
kownika, środowiska uruchomieniowe i biblioteki klas realizujących typowe funkcje usługowe
dla aplikacji. Po drugie, gotowe komponenty dedykowane dziedzinie aplikacyjnej, na przykład
biblioteki realizujące transakcje finansowe, są tworami bardziej konkretnymi niż abstrakcyjne
obiekty modelu analitycznego.
356 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

Rysunek 8.1. Projektowanie obiektów jako proces likwidujący przepaść między obiektami dziedziny
aplikacyjnej określonymi na etapie analizy a gotowymi k o m p o n e n t a m i wybranymi na etapie pro-
jektowania systemu

Po wykonaniu wielu iteracji związanych zarówno z analizą wymagań, jak i projektowa-


niem systemu tworzony system zdaje się przypominać układankę, w której wciąż brakuje wielu
elementów. Owe brakujące elementy mogą być obiektami tworzonymi „od zera", odpowiednio
konfigurowanymi obiektami „z półki" lub precyzyjnie określonymi interfejsami dla każdej
klasy i każdego podsystemu. Model projektowania obiektów podzielony zostaje na zbiory klas,
realizowane niezależnie przez poszczególnych programistów.
Zgodnie z rysunkiem 8.2, etap projektowania obiektów obejmuje cztery grupy aktywności.

• Wykorzystywanie gotowych komponentów i wzorców projektowych. Kompo-


nenty „z półki", zidentyfikowane na etapie projektowania systemu, ułatwiają re-
alizację poszczególnych podsystemów; gotowe biblioteki klas i dodatkowe kom-
ponenty udostępniają realizację podstawowych struktur danych i usług. Wzorce
projektowe ułatwiają rozwiązywanie powszechnych problemów programistycznych
i ochronę wybranych klas przed przyszłymi zmianami. Często gotowe komponenty
muszą być adaptowane do konkretnych potrzeb — dokonuje się tego bądź to za po-
mocą dziedziczenia klas, bądź też przez tworzenie odpowiednich otoczek (wrappers).
W trakcie tej aktywności programiści muszą rozstrzygać liczne dylematy między
samodzielnym tworzeniem „od zera" tego czy innego komponentu a jego realizacją
na bazie gotowych komponentów — ten sam problem mieli zresztą już na etapie
projektowania systemu.
8.2. O projektowaniu obiektów ogólnie 357

• Specyfikowanie interfejsów. Usługi poszczególnych podsystemów, zidentyfikowane


w czasie projektowania systemu, sformułowane zostają w kategoriach interfejsów
poszczególnych klas, czyli zbiorów operacji wraz ze szczegółowym opisem argumen-
tów, sygnatur typu i wyjątków. W ramach tej aktywności często identyfikowane są
dodatkowe operacje i obiekty niezbędne do transferowania danych między podsys-
temami. W ten oto sposób zestaw usług zdefiniowanych dla systemu przeobraża
się w kompletny interfejs tego systemu, zwany często interfejsem programisty,
w skrócie API (Application Programmer Interface).
• Restrukturyzowanie modelu. Restrukturyzacja modelu ma na celu zwiększenie stop-
nia wykorzystywania gotowego kodu oraz realizację innych celów projektowych.
Każda z restrukturyzacji może być reprezentowana w postaci grafu transformacji
podzbiorów konkretnego modelu; do typowych transformacji tego typu należy prze-
kształcanie skojarzeń „jeden na wiele" na skojarzenia „jeden do jednego", implemen-
towanie skojarzeń „jeden do jednego" w postaci referencji, łączenie w pojedynczą
klasę dwóch podobnych klas należących do różnych podsystemów, redukowanie
(„kolapsacja") klas niewykazujących wyraźnego zachowania do postaci atrybutów,
dzielenie skomplikowanych Idas na prostsze klasy i (lub) reorganizowanie klas i ope-
racji w celu wykorzystania dziedziczenia i pakietów. Zabiegi te zmierzają do realizacji
takich celów projektowych jak pielęgnowalność, czytelność i zrozumiałość modelu
systemu.
• Optymalizowanie modelu. Optymalizacja modelu jest środkiem realizacji jego wy-
dajności jako celu projektowego. Wydajność tę mogą zwiększyć takie zabiegi jak
zmiany algorytmów na szybsze lub mniej pamięciożerne, redukowanie krotności
skojarzeń lub dodawanie skojarzeń redundantnych w celu przyspieszenia realizacji
żądań, zmiana kolejności wykonań operacji, dodawanie atrybutów w celu zwiększenia
szybkości dostępu do obiektów czy otwieranie zamkniętej architektury warstwowej
z zamiarem zwiększenia szybkości działania przez bezpośredni dostęp do warstw po-
średnio niższych'.

Projektowanie obiektów nie jest procesem sekwencyjnym. Mimo iż każda grupa wymie-
nionych powyżej aktywności dedykowana jest rozwiązywaniu specyficznego problemu,
aktywności poszczególnych grup realizowane są zazwyczaj równolegle. Gotowy komponent
„z półki" może na przykład narzucać ograniczenie na liczbę typów wyjątków i powodować ko-
nieczność zrewidowania interfejsu podsystemu. Wykorzystywanie gotowych komponentów
przyczynia się do redukcji wysiłku programistów, wymaga jednak zwykle konstruowania nowych
obiektów spajających owe komponenty z resztą systemu. Wreszcie, restrukturyzowanie i opty-
malizowanie modelu może prowadzić do zmniejszenia liczby obiektów wymagających im-
plementacji, dzięki zwiększeniu stopnia wielokrotnego wykorzystywania tego samego kodu.
Zazwyczaj specyfikowanie interfejsów i poszukiwanie możliwości wykorzystywania
gotowych komponentów oraz gotowego kodu odbywa się w pierwszej kolejności. Dostajemy
wówczas model projektu obiektów i weryfikujemy go za pomocą przypadków użycia związa-
nych z konkretnym podsystemem. Restrukturyzacje i optymalizacje są odpowiednie raczej dla

1
Różnica między zamkniętą a otwartą architekturą warstwową wyjaśniona jest w sekcji 6.3.4 — przyp. tłum.
358 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

Rysunek 8.2. Aktywności etapu projektowania obiektów

podsystemu względnie ustabilizowanego. Koncentrując się na interfejsach, kompo-


nentach i wzorcach projektowych, dostajemy model, który łatwo modyfikować; przedwcze-
sne optymalizowanie modelu utrudnia jego późniejszą modyfikację, a co gorsza, odwraca
uwagę od rzeczy istotniejszych. To wszystko nie zmienia jednak ogólnej zasady, że wymie-
nione grupy aktywności przeplatają się ze sobą iteracyjnie.
8.3. Koncepcja wielokrotnego wykorzystywania 359

Wobec bogatego repertuaru aktywności związanych z projektowaniem obiektów, ich


opisywanie podzieliliśmy między trzy rozdziały. Treść tego koncentruje się głównie na wyko-
rzystywaniu gotowych produktów, przede wszystkim komponentów i wzorców projektowych.
W rozdziale 9. „Projektowanie obiektów: specyfikowanie interfejsów" zajmiemy się aktywno-
ściami związanymi ze specyfikowaniem interfejsów oraz językiem ograniczeń OCL (Object
Constraint Language) stanowiącym podzbiór języka UML i jego zastosowaniem do specy-
fikowania niezmienników, warunków wstępnych i warunków końcowych. Rozdział 10. „Od-
wzorowywanie modelu na kod" poświęcimy aktywnościom restrukturalizacyjnym i optymali-
zacyjnym.

8.3. Koncepcja wielokrotnego wykorzystywania


— dziedziczenie, delegowanie i wzorce projektowe
W tej sekcji zajmiemy się koncepcjami związanymi z wykorzystywaniem gotowych rozwiązań,
a szczególnie:

• obiektami aplikacyjnymi i obiektami realizacyjnymi (patrz sekcja 8.3.1),


• różnicą między dziedziczeniem implementacyjnym a dziedziczeniem specyfika-
cyjnym (patrz sekcja 8.3.2),
• delegowaniem (patrz sekcja 8.3.3),
• zasadą zastępowania Liskov (patrz sekcja 8.3.4),
• wykorzystywaniem delegowania i dziedziczenia we wzorcach projektowych (patrz
sekcja 8.3.5).

8.3.1. Obiekty aplikacyjne i obiekty realizacyjne


Jak widzieliśmy w rozdziale 2. „Modelowanie w języku UML", diagramy klas mogą być wyko-
rzystywane do modelowania zarówno dziedziny aplikacyjnej, jak i dziedziny realizacyjnej.
Obiekty dziedziny aplikacyjnej, zwane krótko obiektami aplikacyjnymi, reprezentują kon-
cepcje problemowe związane z tworzonym systemem. Obiekty realizacyjne reprezentują
natomiast komponenty niemające odpowiedników w dziedzinie aplikacyjnej, na przykład
bazy danych, obiekty interfejsu użytkownika czy egzemplarze oprogramowania pośredniego
(middleware).
Na etapie analizy identyfikujemy obiekty encji, relacje między tymi obiektami oraz ich
atrybuty i operacje. Większość obiektów encji to obiekty aplikacyjne, niezależne od konkret-
nego systemu. W czasie analizy identyfikujemy także te obiekty realizacyjne, które są widoczne
dla użytkownika, na przykład obiekty brzegowe i sterujące reprezentujące (odpowiednio) for-
mularze i transakcje definiowane w systemie. Na etapie projektowania systemu identyfikujemy
dalsze obiekty realizacyjne, w kategoriach bliskich platformom sprzętowej i programowej.
W fazie projektowania obiektów doskonalimy obie grupy obiektów — aplikacyjne i realiza-
cyjne — oraz bardziej je uszczegółowiamy, przy okazji identyfikując nowe obiekty realizacyjne,
służące wypełnianiu wspomnianej na wstępie luki projektowej.
360 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

8.3.2. Dziedziczenie implementacyjne i dziedziczenie specyfikacyjne


W czasie analizy wymagań wykorzystujemy mechanizm dziedziczenia klas do klasyfiko-
wania obiektów w ramach taksonomii. Pozwala to odróżnić wspólne elementy zachowania,
charakterystyczne dla ogólnego przypadku, czyli superklasy (zwanej także klasą bazową), od
zachowania charakterystycznego dla poszczególnych specjalizowanych obiektów, będących
egzemplarzami subklas (zwanych także klasami pochodnymi). Celem generalizacji (czyli
identyfikowania superklasy dla danego zbioru istniejących klas) i specjalizacji (czyli identyfi-
kowania poszczególnych subklas, pochodnych od danej superklasy) jest zorganizowanie
obiektów modelu analitycznego w czytelną i zrozumiałą hierarchię. Dzięki temu czytelnikowi
zapoznającemu się z modelem analitycznym funkcjonalność systemu jawi się początkowo
jako zbiór ogólnych koncepcji, które w miarę lektury zostają coraz bardziej konkretyzowane.
Przykładowo w modelu analitycznym wielokrotnie już cytowanego systemu FRIEND, opi-
sywanego dokładnie w rozdziale 4. „Zbieranie wymagań", najpierw skupiamy się na ogólnej
strategii reagowania na wypadki różnych rodzajów, a dopiero później zwracamy uwagę na
różnice między reagowaniem na wypadki drogowe a reagowaniem na pożary.
Celem wykorzystywania dziedziczenia klas na etapie projektowania obiektów jest zmniej-
szanie redundancji modelu i zwiększanie jego rozszerzalności. Wyodrębniając powtarzające
się (redundantne) w poszczególnych klasach elementy zachowania w formie jednej superklasy,
zmniejszamy ryzyko spowodowania niespójności związanej z wprowadzaniem zmian w opisie
tego zachowania (na przykład zmian wynikających z poprawiania usterek), bo zmiany te do-
tyczyć będą wówczas tylko jednego miejsca — wspomnianej superklasy. Udostępniając apli-
kacji abstrakcyjny interfejs (zdefiniowany w abstrakcyjnej superklasie) jako jedyny środek
komunikowania się z obiektami subklas, zapewniamy sobie łatwość rozbudowy, polegającą na
możliwości definiowania nowych subklas, bez jakichkolwiek zmian we wspomnianym in-
terfejsie. I tak na przykład, tworząc aplikację dedykowaną zarządzaniu kolekcją obrazków,
definiujemy klasę abstrakcyjną Image, w ramach której specyfikujemy wszystkie operacje
związane z obrazkami w ogólności, niezależnie od ich typów; zachowanie specyficzne dla da-
nego typu obrazka specyfikujemy wówczas w subklasie reprezentującej ten typ (na przykład
GIFImage, JPEGImage). Gdy w przyszłości będziemy chcieli wzbogacić funkcjonalność apli-
kacji o obsługę nowych typów obrazków, wystarczy, że zdefiniujemy nowe specjalizowane
subklasy odpowiadające tym typom.
Mimo iż wykorzystywanie dziedziczenia może sprawić, że model analityczny stanie się
bardziej zrozumiały, a model projektu obiektów — łatwiejszy w modyfikowaniu i rozszerza-
niu, korzyści te nie pojawiają się samorzutnie: dziedziczenie jest mechanizmem tak silnym, że
nowicjuszom zdarza się przy jego użyciu tworzyć kod udziwniony i zawiły. Załóżmy dla przy-
kładu, że programista nowicjusz nie wie (lub zapomniał) o istnieniu w języku Java abstrakcji
Set i postanawia utworzyć własną klasę reprezentującą zbiór elementów. Ponieważ funkcje
magazynowania obiektów obecne są w tablicy haszowanej, nasz programista zamierza użyć
klasy Hashtable jako punktu wyjścia dla swojej klasy MySet. Sprawdzenie, czy dany element
jest obecny w zbiorze, reprezentowane przez funkcję contai nsVal ue(), byłoby realizowane
za pomocą odziedziczonej metody contai nsKey (). Zarys tej implementacji widzimy w lewej
części rysunku 8.3 i na listingu 8.1.
8.3. Koncepcja wielokrotnego wykorzystywania 361

Rysunek 8.3. Przykład dziedziczenia implementacyjnego. Wątpliwa implementacja widoczna w lewej


kolumnie zostaje zamieniona na bardziej sensowną, wykorzystującą delegowanie

Listing 8.1. Implementacja klasy MySet przy użyciu dziedziczenia implementacyjnego

class MySet extends Hashtable {


MySet() {
... konstruktor ...
}
void put(Object element) {
if (!containsKey(element)){
put(e1ement, t h i s ) ;
}
}
boolean containsValue(Object element)!
return c o n t a i n s K e y ( e ł e m e n t ) ;
}
... inne metody ...
}

Wspaniale! Mamy wszystko, czego potrzeba, bez wielkiego wysiłku — wszystko, czego
potrzebujemy, wzięliśmy żywcem z klasy Hashtabl e. Hojne dziedziczenie okazało się jednak
nazbyt hojne i przez to zdradliwe: nasza klasa MySet posiada bowiem także elementy, które
mogą stać się przyczyną poważnych kłopotów. Zauważmy, że klasa MySet dziedziczy po klasie
Hashtable metodę containsKey(), która nie dość, że nie jest do niczego potrzebna, to do-
datkowo staje się przysłowiową kulą u nogi: funkcjonuje ona identycznie z (przedefiniowaną)
metodą containsValue(), więc programista wykorzystujący nową klasę ma prawo używać
tych dwóch metod zamiennie. Gdy w przyszłości zaimplementujemy klasę MySet w inny sposób,
362 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

na przykład na bazie listy wiązanej, nieposiadającej metody containsKey(), kod aplikacji wy-
magać może daleko idących zmian. Owszem, można by przedefiniować metodę contai nsKey ()
tak, by jej wywoływanie generowało wyjątek, ale czytelności i zrozumiałości kodu raczej to nie
poprawi.
Dziedziczenie ma w sobie coś z obosiecznego miecza: z jednej strony, zmniejsza powią-
zanie między podobnymi klasami, „wyciągając" z nich wspólną funkcjonalność do wspólnej
superklasy, z drugiej jednak, generuje silne powiązanie tej superklasy z każdą z jej subklas.
I podczas gdy jest to zjawisko jak najbardziej pożądane w przypadku budowania hierarchii
opartej na taksonomii (jak w przypadku wspomnianej kolekcji obrazków), powiązanie takie
staje się kłopotliwe w innych sytuacjach. W naszym przykładzie relacja dziedziczenia wiąże ze
sobą klasy Hashtable i MySet, reprezentujące dwie różne koncepcje, co niesie ze sobą nieko-
rzystne konsekwencje co najmniej pod dwoma względami: po pierwsze, wszelkie modyfikacje
klasy Hashtable automatycznie odbijać się będą na klasie MySet, po drugie, potraktowanie
klasy MySet jako konkretyzacji (specjalizacji) klasy Hashtable może dawać dziwaczne re-
zultaty w przypadku zastąpienia (w kodzie programu) obiektu klasy Hashtable przez obiekt
klasy MySet. Wszystko to wynika z braku taksonomii między obiema klasami — po prostu
zbiór elementów nie jest szczególnym przypadkiem tablicy haszowanej.
Jeżeli mimo to sięgnęliśmy po dziedziczenie, zrobiliśmy to z zamiarem wykorzystania
gotowego kodu, gotowej implementacji. Relacja dziedziczenia, wykorzystywana w takim wła-
śnie celu, mimo różnic koncepcyjnych między powiązanymi klasami, nazywa się dziedzicze-
niem implementacyjnym. Zamiast pisać od zera własny kod nowej klasy, wykorzystuje się
gotowy, wzięty z innej klasy, co najwyżej poddając go niewielkim modyfikacjom. Dla odróż-
nienia, dziedziczenie mające swe odzwierciedlenie w taksonomii klas nazywane jest dzie-
dziczeniem specyfikacyjnym lub dziedziczeniem interfejsu. Widoczny na rysunku 8.4 dia-
gram klas ilustruje zależności między czterema różnymi typami dziedziczenia.

Rysunek 8.4. Metamodel dziedziczenia. W projektowaniu zorientowanym obiektowo dziedziczenie wy-


korzystywane jest z myślą o osiągnięciu różnych celów, między innymi do modelowania taksonomii lub
wykorzystywania gotowych elementów zachowania innych klas. W pierwszym przypadku relacja dzie-
dziczenia może być identyfikowana w drodze bądź to specjalizacji (gdy na bazie klasy ogólnej definiowa-
ne są klasy bardziej szczegółowe), bądź generalizacji (klasa ogólna tworzona jest jako abstrakcja ujmująca
wspólne elementy istniejących klas specjalizowanych). W drugim przypadku, gdy przez dziedziczenie klas
zamierzamy osiągnąć powtórne wykorzystanie gotowego kodu, dziedziczenie to może mieć charakter spe-
cyfikacyjny (gdy jedna klasa jest podtypem drugiej) albo implementacyjny (gdy dwie Masy nie mają ze so-
bą nic wspólnego pod względem koncepcyjnym, a łączą je tylko identyczne lub podobne fragmenty kodu)
8.3. Koncepcja wielokrotnego wykorzystywania 363

8.3.3. Delegowanie
Alternatywą dla dziedziczenia implementacyjnego jest delegowanie implementacji. Zamiast
wykorzystywać określoną metodę konkretnej klasy poprzez dziedziczenie, wywołujemy ją
najzwyczajniej, a dokładniej — wywołuje ją obiekt lub klasa, do którego to wywołanie delegu-
jemy. Na listingu 8.2 pokazaliśmy, jak można wykorzystać metodę containsKeyO klasy
Hashtabl e, nie wprowadzając w ogóle dziedziczenia po tej klasie: jak widać, jedyną znaczącą
zmianą jest pojawienie się prywatnego pola tabl e i jego inicjacja przez konstruktor; w wy-
niku wspomnianej inicjacji pole to wskazuje na obiekt klasy Hashtable, do którego metoda
contai nsVal ue () deleguje wywołanie metody contai nsKey (). Odzwierciedlenie tej sytuacji na
diagramie klas widoczne jest w prawej części rysunku 8.3.

Listing 8.2. Implementacja klasy MySet przy użyciu delegowania

class MySet {
private Hashtable t a b l e ;

MySet () {
t a b l e = Hashtabl e ( ) ;
}
void put(Object element) {
if (!containsValue(element)){
table.put(element,this);
}
}
boolean containsValue(Object element) {
return (table.containsKey(element));
)
... inne metody ...

Delegowanie uwidacznia jawną zależność między klasą nowo definiowaną a klasą, do


której deleguje się implementację metody. Likwiduje to oba problemy, które niosło ze sobą
dziedziczenie implementacyjne, czyli:

• rozszerzalność — klasa MySet w nowej implementacji nie zawiera metody contai ns


^KeyO w swym interfejsie, a obiekt, do którego deleguje się jej wywołanie, należy
do sfery prywatności klasy; możemy zatem dowolnie manipulować implementacją
klasy (na przykład zamieniając tablicę haszowaną na listę wiązaną) bez wpływu na
klientów tej klasy,
• podtypowanie — klasa MySet nie wywodzi się z klasy Hashtabl e, nie może więc być
jej substytutem w kodzie programu. Zdefiniowanie klasy MySet nie stanowi więc żad-
nego zagrożenia dla poprawności kodu używającego obiektów klasy Hashtabl e.
364 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

Delegowanie jest preferowanym mechanizmem w stosunku do dziedziczenia implemen-


tacyjnego, nie interferuje bowiem z istniejącymi komponentami i daje w rezultacie bardziej so-
lidny kod. Dziedziczenie specyfikacyjne jest natomiast lepsze od delegowania w sytuacji podty-
powania, bo prowadzi do bardziej elastycznego, rozszerzalnego projektu.

8.3.4. Zasada zastępowania Liskov


Zasada zastępowania Liskov [Liskov, 1988] określa formalną definicję dziedziczenia spe-
cyfikacyjnego. Jeżeli mianowicie w kodzie klienta występuje odwołanie do metody definiowa-
nej w superklasie, wywoływanej na rzecz egzemplarza tej superklasy lub klasy pochodnej, to
egzemplarz ten zastąpić można egzemplarzem dowolnej innej subklasy, pochodnej od tej sa-
mej superklasy. Przykładowo w odniesieniu do implementacji klasy MySet przedstawionej na
listingu 8.1 oznacza to, że jeżeli w kodzie klienckim występuje odwołanie do obiektu klasy
Hashtabl e, można je zastąpić odwołaniem do obiektu klasy MySet, bez modyfikowania samego
kodu klienckiego. Jak wiemy, nie jest to prawdą — i dlatego relacja dziedziczenia między kla-
sami MySet i Hashtable nie jest dziedziczeniem specyfikacyjnym. Oto formalna definicja ty-
tułowej zasady.

Zasada zastępowania Liskov


Jeśli obiekt klasy S może stać się substytutem obiektu klasy T w dowolnym miejscu kodu, w którym
oczekiwany jest obiekt klasy T, to klasa S jest podtypem klasy T.

Interpretacja
W odniesieniu do projektowania obiektowego zasada zastępowania Liskov oznacza, że jeśli wszystkie
klasy są podtypami swych superldas, to wszystkie relacje dziedziczenia między klasami są dziedzi-
czeniem specyfikacyjnym. Innymi słowy, wszystkie metody definiowane w danej superklasie mogą
być wywoływane zarówno na rzecz obiektów tejże superklasy, jak i obiektów jej dowolnej subklasy
— bez jakiejkolwiek informacji o tym, z którą klasą mamy aktualnie do czynienia. W praktyce ozna-
cza to, że dla dowolnej klasy możemy dowolnie definiować jej nowe subklasy, bez wpływu na kod
kliencki; w ten sposób realizuje się postulat rozszerzalności systemu. Jeżeli wszystkie subklasy dzie-
dziczą po swej superklasie, zgodnie z zasadą Liskov, mówimy wówczas o ścisłym dziedziczeniu.

8.3.5. Delegowanie i dziedziczenie we wzorcach projektowych


Rozstrzyganie dylematu między użyciem dziedziczenia a delegowaniem implementacji nigdy
jest sprawą oczywistą i wymaga pewnego doświadczenia. Obie techniki — dziedziczenie i de-
legowanie — używane w rozmaitych kombinacjach ułatwić mogą rozwiązywanie wielu pro-
blemów, między innymi oddzielanie abstrakcyjnych interfejsów od ich implementacji, two-
rzenie otoczek wokół przestarzałego kodu czy oddzielanie klas ustanawiających reguły od klas
implementujących mechanizmy wymuszania tych reguł.
Na gruncie obiektowo zorientowanego tworzenia oprogramowania wzorce projektowe
są szablonowymi rozwiązaniami, które programiści mogą, po odpowiednim przystosowaniu,
wykorzystywać do rozwiązywania powtarzających się problemów [Gamma i in., 1994], Każdy
wzorzec projektowy definiowany jest w postaci czterech następujących elementów:
8.3. Koncepcja wielokrotnego wykorzystywania 365

1. nazwy jednoznacznie identyfikującej go wśród innych wzorców projektowych,


2. opisu problemu, czyli wskazania sytuacji, w której zastosowanie wzorca może być
użyteczne; sytuacje takie najczęściej związane są z postulatami modyfikowalności
i rozszerzalności systemu, jak również z dotyczącymi systemu wymaganiami po-
zafunkcyjnymi,
3. rozwiązania mającego postać zbioru współpracujących klas i interfejsów,
4. zbioru konsekwencji opisujących kompromisy i alternatywne rozwiązania w stosunku
do problemów, dla jakich użyteczny bywa dany wzorzec.

Nawiązując do definiowania nowej Idasy MySet (patrz rysunek 8.3), chcielibyśmy uzy-
skać jej zgodność z istniejącym interfejsem (czy interfejsem Set języka Java), przy jednocze-
snym wykorzystaniu funkcjonalności kryjącej się w klasie Hashtable. Zarówno interfejs Set,
jak i klasa Hashtabl e są już zdefiniowane i ich definicji zmienić nie można, aby więc zrealizować
nasze zadanie, posłużymy się wzorcem projektowym o nazwie Adapter (patrz tabela 8.1
i sekcja A.2), szablonowym dla rozwiązywania problemów tego typu. Wzorzec ten funkcjonuje
następująco: ldasa adaptera (nazwijmy ją umownie Adapter) implementuje wszystkie metody
zadeklarowane w klasie klienckiej (Cl i e n t l n t e r f a c e ) w kategoriach żądań wysyłanych do
klasy istniejącej i niezmiennej (LegacyCl ass). Wszelkie konwersje danych, zachowania i tym
podobne właściwe dla klasy LegacyCl ass wykonywane są wewnątrz klasy Adapter, która
na zewnątrz zachowuje się dokładnie tak, jak oczekuje tego klient na podstawie interfejsu
Cl i e n t l n t e r f a c e . Tak oto udaje się pogodzić dwa niekompatybilne byty — interfejs Cl ient
"-•Interface i klasę LegacyCl ass — bez potrzeby modyfikowania któregokolwiek z nich.
Ponieważ ten sam Adapter może być użyty do wszelkich podtypów klasy LegacyCl ass
(zgodnie z zasadą zastępowania Liskov), wzorzec projektowy Adapter realizuje postulat roz-
szerzalności systemu. Odnosząc ten wzorzec do klasy MySet czyniącej zadość interfejsowi Set,
otrzymamy schemat odpowiadający delegowaniu przedstawionemu na listingu 8.2 (patrz
rysunek 8.5).
Zauważmy, że wzorzec projektowy Adapter wykorzystuje zarówno dziedziczenie, jak
i delegowanie. Reguła ta odnosi się zresztą do wielu wzorców projektowych, które na pierwszy
rzut oka mogą z tego powodu wyglądać łudząco podobnie, lecz jednak te same mechanizmy
używane są przez każdy z nich na różne sposoby, często różniące się subtelnymi szczegółami.
By zrozumieć te różnice, używać będziemy następujących określeń na poszczególne klasy
uwikłane we wzorzec projektowy.

• Klasa kliencka to klasa korzystająca ze wzorca. Na diagramie klas zawierającym


wzorzec projektowy Adapter (jak na rysunku wewnątrz tabeli 8.1), klasa ta zwy-
czajowo nosi nawę Cl i ent. Może to być zarówno klasa wchodząca w skład goto-
wej biblioteki, jak i nowa klasa powstającego systemu.
• Interfejs wzorca to część wzorca widoczna dla klasy klienckiej. Fizycznie realizowany
jest w postaci interfejsu (w znaczeniu programowania obiektowego) lub klasy abs-
trakcyjnej. W ramach wzorca projektowego Adapter interfejs ten nosi zwyczajowo
nazwę Cl i e n t l n t e r f a c e .
366 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

Tabela 8.1. Przykład wzorca projektowego Adapter (zaczerpnięty z książki E. Gammy, R. Heima,
R. Johnsona i J. Ylissadesa [Gamma i in., 1994])

Nazwa Wzorzec projektowy Adapter

Opis problemu Konwersja interfejsu prezentowanego przez istniejącą klasę na inny interfejs
narzucony przez klienta, bez potrzeby modyfikowania zarówno wspomnianej
klasy, jak i wymagań klienta.

Rozwiązanie Klasa Adapter implementuje interfejs C l i e n t narzucony przez klienta, delegując


wywołania metod do klasy LegacyCl a s s i przeprowadzając wszelkie niezbędne
konwersje.

Konsekwencje ' Interfejs Cl i ent i klasa LegacyCl ass mogą współpracować ze sobą bez żadnych
modyfikacji.
• Ten sam Adapter może być zastosowany do wszelkich subklas klasy LegacyCl ass
• Dla każdej specjalizacji (subklasy) interfejsu Cl i ent konieczne jest utworzenie
osobnego Adaptera.

Rysunek 8.5. Zastosowanie wzorca projektowego Adapter do problemu implementacji klasy MySet
z rysunku 8.3

• Klasa implementacyjna to klasa realizująca niskopoziomowe zachowanie wzorca.


W ramach wzorca projektowego Adapter klasami implementacyjnymi są klasy
Adapter i LegacyCl ass. W wielu wzorcach projektowych do implementacji pożąda-
nego zachowania konieczny jest cały zbiór klas implementacyjnych.
8.4. Wybór wzorców projektowych i gotowych komponentów 367

• Klasa rozszerzająca to specjalizacja klasy implementującej dostarczająca odmienną


implementację lub rozszerzone zachowania wzorca. W ramach wzorca projektowego
Adapter rolę klas rozszerzających mogą pełnić klasy będące podtypami klasy Legacy
^ C l a s s . Klasy rozszerzające najczęściej reprezentują przyszłe zmiany, przewidy-
wane zawczasu przez programistów.

Wzorce projektowe stanowią jedynie wskazówki postępowania, nie są czymś w rodzaju


gotowych bibliotek i nie dostarczają gotowej recepty rozwiązywania konkretnych problemów.
Ponieważ jednak ucieleśniają pokaźny zbiór wiedzy (między innymi w postaci określenia
sytuacji, w których dany wzorzec może być przydatny, oraz kompromisów związanych z jego
zastosowaniem), stanowią doskonałe przykłady właściwego korzystania z dziedziczenia i de-
legowania implementacji.
W następnej sekcji dokonamy pobieżnego przeglądu najczęściej stosowanych wzorców
projektowych w kontekście typowych problemów, w rozwiązywaniu których mogą okazać się
pomocne.

8.4, Wybór wzorców projektowych i gotowych komponentów


Projektowanie systemu i projektowanie obiektów kryje w sobie pewien paradoks. Z jednej
strony, na etapie projektowania systemu stawiamy solidne ściany między podsystemami, ogra-
niczając w ten sposób wpływ poszczególnych podsystemów na siebie i chroniąc każdy podsystem
przed konsekwencjami zmian w innych podsystemach. Z drugiej jednak strony, na etapie pro-
jektowania obiektów dążymy do tego, by sprawić, żeby oprogramowanie stało się maksymalnie
modyfikowalne i rozszerzalne, z zamiarem zminimalizowania kosztu przyszłych zmian. Kie-
rujemy się zatem dwoma sprzecznymi celami: oczekujemy architektury stabilnej (by uporać się
ze złożonością), ale też chcemy, by była ona elastyczna (aby można było ją w przyszłości łatwo
przystosowywać do nieuniknionych zmian). Sprzeczność tę można rozwiązać, projektując
wspomnianą architekturę z uwzględnieniem (przewidywaniem) wspomnianych zmian — dobrą
wiadomością jest to, że przyczyny tych zmian są z grubsza te same dla większości systemów.

• Nowi dostawcy lub nowe technologie. Komercyjne komponenty służące do budo-


wania aplikacji zyskują sobie z biegiem czasu rozmaitych, bardziej atrakcyjnych,
konkurentów. Zmiany tego typu są trudne do przewidzenia, podobnie jak dyna-
mizm samego rynku — bywa że producent wykorzystywanego komponentu znika
z rynku jeszcze przed ukończeniem projektu.
• Zmiany w implementacji. Etap integrowania podsystemów okazuje się chwilą prawdy
w kwestii wydajności systemu, a szczególnie jego reaktywności: częściej niż rzadziej
okazuje się, że czas odpowiedzi systemu plasuje się wyraźnie powyżej ograniczenia
określonego przez wymagania pozafunkcyjne. Wydajność systemu jest czynnikiem
trudnym do przewidzenia i programiści nawet nie próbują jej specjalnie optymali-
zować przed etapem integracji. Aby spełnić pokładane w systemie oczekiwania, ko-
nieczne jest późniejsze ponowne implementowanie niektórych usług, z wykorzysta-
niem bardziej efektywnych algorytmów i struktur danych.
368 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

• Nowe widoki. Testowanie systemu przez jego użytkowników może obnażyć wiele
problemów związanych z użytecznością. Ich rozwiązywanie może wymagać stworze-
nia dodatkowych widoków dla istniejących danych.
• Zmiany w zakresie dziedziny aplikacyjnej. Podczas wdrażania systemu mogą obja-
wić się nowe pomysły o charakterze generalizacyjnym: przykładowo udane wdrożenie
systemu bankowego w jednym oddziale może stanowić impuls do stworzenia po-
dobnego systemu zarządzającego wieloma oddziałami równocześnie. Sama dziedzina
aplikacyjna też może ulegać przeobrażeniom: przykładowo, o ile poprzednio każdy
samolot identyfikowany był pojedynczym numerem lotu, to teraz, w konsekwencji
współpracy przewoźników, ten sam lot może być identyfikowany w inny sposób
u każdego przewoźnika i z pojedynczym samolotem może być w danej chwili zwią-
zany nie jeden, ale kilka różnych numerów lotu.
• Błędy. Gdy użytkownicy przystąpią do testowania systemu, mogą uwidocznić się błędy
(nawet liczne) w wymaganiach stawianych temu systemowi.

Wykorzystywanie dziedziczenia i delegowania, w połączeniu z klasami abstrakcyjnymi


umożliwia oddzielenie interfejsu podsystemu od jego aktualnej implementacji. W tej sekcji
pokażemy na kilku przykładach, jak przy użyciu wzorców projektowych można skutecznie
radzić sobie ze zmianami powodowanymi przez wymienione powyżej przyczyny. Po związa-
niu określonej przyczyny z najbardziej dla niej stosownym wzorcem projektowym (patrz
tabela 8.2) opiszemy poszczególne wzorce w kontekście konkretnego problemu i dla każdego
z nich pokażemy związek z dziedziczeniem i delegowaniem oraz wpływ tego związku na mo-
dyfikowalność i rozszerzalność systemu.

8.4.1. Hermetyzacja przechowywania danych


za pomocą wzorca projektowego Most
Rozpatrzmy problem przyrostowego tworzenia, testowania i integrowania podsystemów bu-
dowanych przez różnych programistów. Poszczególne podsystemy kończone są w różnym
czasie, co wymaga wstrzymywania się z ich integrowaniem do czasu ukończenia ostatniego.
Aby unikać takich opóźnień, wykorzystuje się często atrapy podsystemów zamiast ich realnej
postaci, dzięki czemu testy integracyjne mogą rozpocząć się wcześniej. Często także programi-
ści posługują się kilkoma implementacjami tego samego podsystemu: implementacja „refe-
rencyjna", używająca podstawowych algorytmów, prosta, czytelna i prawdopodobnie poprawna,
jest jednak mniej wydajna od implementacji optymalizowanej pod kątem wydajności, lecz
bardziej skomplikowanej i mniej oczywistej. Tak czy inaczej, stajemy wobec konieczności dy-
namicznego „przełączania się" między różnymi realizacjami tego samego interfejsu, w celu wy-
konywania odmiennych zadań.
Do radzenia sobie z problemami tego rodzaju służy wzorzec projektowy Most (Bridge)
(patrz sekcja A.3 i książka E. Gammy, R. Heima, R. Johnsona i J. Vlassidesa [Gamma i in., 1994]).
Rozpatrzmy dla przykładu przechowywanie danych lig (League) w systemie ARENA. We wcze-
snych etapach tworzenia tego systemu interesuje nas jedynie najbardziej podstawowy mecha-
nizm przechowywania danych bazujący na serializacji obiektów, zależy nam bowiem na uzy-
skaniu środowiska umożliwiającego testowanie i debugowanie głównych przypadków użycia
8.4. Wybór wzorców projektowych i gotowych komponentów 369

Tabela 8.2. Wybrane wzorce projektowe w powiązaniu z przyczynami zmian, przy których wprowa-
dzaniu mogą okazać się pomocne

Wzorzec Przewidywana przyczyna zmian Sekcje opisujące


projektowy i mechanizm wzorca wzorzec
Most Nowy dostawca, nowa technologia, nowa implementacja. 8.4.1, A.3
Izoluje interfejs klasy od jej implementacji. Służy temu
s a m e m u celowi, co wzorzec p r o j e k t o w y Adapter z tą
różnicą, że programista nie jest ograniczany przez
istniejący k o m p o n e n t .

Adapter Nowy dostawca, nowa technologia, nowa implementacja. 8.4.2, A.2


Hermetyzuje fragment starego kodu, nieprzystosowanego
do współpracy z n o w y m środowiskiem. Ogranicza
także wpływ zastąpienia wspomnianego fragmentu przez
inny komponent.

Strategia Nowy dostawca, nowa technologia, nowa implementacja. 8.4.3, A.9


Izoluje algorytm od jego implementacji. Służy temu samemu
celowi, co wzorce projektowe Most i Adapter z tą różnicą,
że hermetyzacji podlega określony aspekt zachowania,
a nie konkretny komponent.

Fabryka Nowy dostawca, nowa technologia. Hermetyzuje tworzenie 8.4.4, A.l


abstrakcyjna poszczególnych rodzin obiektów, oddzielając klienta od
procesu ich tworzenia, zapobiegając jednocześnie używaniu
obiektów należących do różnych (niekompatybilnych ze sobą)
rodzin.

Polecenie Nowa funkcjonalność. Izoluje obiekty odpowiedzialne za 8.4.5, A.4


przetwarzanie poleceń od samych poleceń, chroniąc te
obiekty przed zmianami wynikającymi z modyfikowania
funkcjonalności systemu.

Kompozyt Nowe elementy w dziedzinie aplikacyjnej. H e r m e t y z u j e


hierarchie, dostarczając wspólną superklasę dla agregatów
i węzłów-liści. Nowe typy liści można dodawać bez
modyfikowania istniejącego kodu.

dla podsystemu TournamentManagement. Obiekty encji będą doznawać jeszcze wielu zmian
i na tym etapie trudno zidentyfikować wszelkie „wąskie gardła" związane z ich przechowywa-
niem. W konsekwencji efektywny system przechowywania danych systemu nie jest na tym etapie
zagadnieniem numer 1 i w pierwszym prototypie systemu poprzestajemy na mechanizmie
„płaskich" plików. Jak jednak pisaliśmy w sekcji 7.6.4, w drugim prototypie przewidujemy jednak
opcję zastosowania relacyjnej bazy danych obok wspomnianych „płaskich" plików. Aby jak
najwcześniej można było rozpocząć testy integracyjne, przed ukończeniem implementowania
mechanizmów opartych na owych „płaskich" plikach potrzebne będą ich atrapy.
Zastosowanie wzorca projektowego Most do rozwiązania tego problemu przedstawione
jest na rysunku 8.6. LeagueStore jest klasą interfejsu tego wzorca, która dostarcza wysokopo-
ziomową funkcjonalność związaną z przechowywaniem danych. LeagueStorelmpl ementor
370 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

Rysunek 8.6. Zastosowanie wzorca projektowego Most do abstrahowania od konkretnej implementacji


mechanizmów przechowywania danych w systemie ARENA

jest wspólnym, abstrakcyjnym interfejsem dla trzech implementacji: StubStorelmpl ementor


(dla atrap), XMLStorelmpl ementor (dla „płaskich plików")2 i JDBCStorelmpl ementor dla rela-
cyjnej bazy danych (osiągalnej za pośrednictwem JDBC).
Zauważmy, że nawet wówczas gdy większość klas LeagueStorelmpl ementor dostarcza
podobne usługi, wykorzystywanie wzorca projektowego Most pogarsza wydajność systemu.
Stajemy tym samym przed wyborem między wydajnością a modyfikowalnością, jako celami
projektowymi określonymi w sekcji 6.4.2.

Dziedziczenie i delegowanie we wzorcu projektowym Most

Interfejs wzorca projektowego Most realizowany jest przez klasę3 Abstract! on*, zaś jego
zachowanie — przez jedną z klas Concretelmpl ementor-. Wzorzec może być rozszerzany
przez dostarczanie nowych klas RefinedAbstraction- lub Concretelmpl ementor. Stanowi on
klasyczny przykład połączenia dziedziczenia specyfikacyjnego z delegowaniem, dzięki czemu
zapewnia zarówno wykorzystywanie gotowych rozwiązań, jak i elastyczność. Z jednej strony,
relacja dziedziczenia specyfikacyjnego wiąże abstrakcyjny interfejs Impl ementor z klasami
Concretelmpl ementor, zatem każda z tych ostatnich może w programie stanowić substytut dla
klas Abstraction- i RefinedAbstraction-. Daje to jednocześnie zapewnienie, że programi-
ści, opracowując klasę Concretelmpl ementor, będą się starali upodobnić jej zachowanie do za-
chowania innych klas tej grupy. Z drugiej strony, między klasami Abstraction- i Impl ementor
istnieje relacja delegowania, co zapewnia dystrybucję różnego zachowania po obu stronach
mostu. Przykładowo klasa LeagueStore z rysunku 8.6 reprezentuje wysokopoziomowe aspekty
przechowywania danych związanych z poszczególnymi ligami, podczas gdy każda z klas grupy

2
W każdym „płaskim" pliku przechowywany będzie obraz jednego lub więcej obiektów, w formacie
XML — przyp. tłum.
3
W opisach zastosowań poszczególnych wzorców projektowych autorzy używają nazw pochodzących
z dwóch obszarów: bieżących rysunków rozdziału oraz terminologii zawartej w dodatku A. Dla lepszej
czytelności nazwy tej drugiej grupy wyróżnione są gwiazdką — przyp. tłum.
8.4. Wybór wzorców projektowych i gotowych komponentów 371

LeagueStorelmpl ementor dostarcza odmienną, niskopoziomową funkcjonalność, realizującą


konkretny mechanizm tego przechowywania. Ponieważ klasa LeagueStorelmpl ementor pre-
zentuje zachowanie odmienne niż klasa LeagueStore, nie może być uważana za jej podtyp,
zgodnie z zasadą Liskov.

8.4.2. Hermetyzacja niekompatybilnych komponentów


za pomocą wzorca projektowego Adapter
W miarę jak systemy informatyczne stają się coraz bardziej złożone, a czas dostarczania ich
na rynek — coraz krótszy, oprogramowanie staje się droższe od sprzętu. Programiści dążą
więc do jak największego wykorzystywania kodu z poprzednich projektów i stosowania goto-
wych komponentów. Systemy interaktywne na przykład rzadko budowane są dziś „od zera":
ich osnowę stanowią narzędzia służące do tworzenia interfejsu użytkownika, udostępniające
szeroką gamę okien, dialogów, przycisków i innych obiektów spotykanych w typowym inter-
fejsie. Gdy trzeba zmienić interfejs użytkownika, modyfikowana jest zwykle tylko niewielka
część systemu: przykładowo systemy zarządzania przedsiębiorstwami, drogie i czasochłonne
w tworzeniu, muszą być sukcesywnie uaktualniane w miarę unowocześniania sprzętu klienc-
kiego. Często tylko ldiencka strona takiego systemu aktualizowana jest pod kątem nowych tech-
nologii, jego zaplecze pozostaje nienaruszone. I w tym momencie natrafiamy na problem
kompatybilności: programiści muszą poradzić sobie z komponentami, które nie pasują do nowej
technologii, a nie są — niestety — modyfikowalne.
Skoro nie można ich modyfikować do pożądanej postaci, nie pozostaje nic innego, jak
zamknąć je w odpowiedniej otoczce, która tę nową postać będzie symulować. Otoczka taka
spełni rolę przegrody, oddzielającej istniejące komponenty od reszty systemu, dzięki czemu
komponenty te nie będą nakładać na nowy projekt krępujących ograniczeń.
W realizacji takiego podejścia pomocny okazuje się wzorzec projektowy Adapter
(patrz sekcja A.2 i przywoływana już tu książka E. Gammy i pozostałych autorów [Gamma
i in., 1994]). Istotą tego wzorca jest konwersja interfejsu istniejącego komponentu do po-
staci, jaka wymagana jest przez jego klienta. Ów wymagany przez klienta interfejs nosi nazwę
Clientlnterface- na rysunku wewnątrz tabeli 8.1. Klasa Adapter- pełni rolę spoiwa mię-
dzy tym interfejsem a klasą LegacyCl ass- reprezentującą interfejs istniejącego komponentu.
Załóżmy teraz, że klientem jest statyczna metoda s o r t ( ) klasy Array (w języku Java). Wy-
wołanie tej metody wymaga dwóch parametrów: tablicy obiektów i komparatora (obiektu
Comparator) dostarczającego metodę compare() porównującą elementy tablicy, czyli defi-
niującą względny porządek dwóch wskazanych elementów. Niech obiektami tablicy będą
obiekty klasy MyString, definiującej metody gretaerThan() („większy od ...") i equals()
(„równe"). By tę tablicę posortować, potrzebujemy komparatora MyStringComparator, którego
metoda compare () oparta jest na tych dwu metodach. Klasa tego komparatora jest właśnie
adapterem 4 . Zastosowanie wzorca Adapter do opisanego przypadku przedstawiliśmy na ry-
sunku 8.7, zaś definicja klas MyStri ng i MyStri ngComparator widoczna jest na listingu 8.3.

4
Przy tworzeniu systemu całkowicie „od zera" rzadko zdarza się, że trzeba użyć adapterów z tej
prostej przyczyny, że nowe ldasy definiować można od razu zgodnie z wymaganymi interfejsami.
372 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

Rysunek 8.7. Zastosowanie wzorca projektowego Adapter do sortowania tablicy łańcuchów; patrz
także listing 8.3

Listing 8.3. Przykład zastosowania wzorca projektowego Adapter w języku Java. Statyczna metoda s o r t ()
klasy A r r a y wymaga dwóch a r g u m e n t ó w : s o r t o w a n e j tablicy e l e m e n t ó w (a) oraz k o m p a r a t o r a
porównującego elementy (c). Aby posortować tablicę elementów klasy MyStri ng, potrzebujemy komparatora
MyStri ngComparator zgodnego z interfejsem Comparator. Klasa MyStri ngComparator jest adapterem

/* istniejący interfejs docelowy */


interface Comparator {
int c o m p a r e ( O b j e c t o l , O b j e c t o 2 ) ;

}
/* istniejący klient */
class Array {
static void s o r t ( 0 b j e c t [] a , Comparator c);

/* istniejąca klasa adaptowana */


class MyString extends S t r i n g {
boolean e q u a l s ( O b j e c t o ) ;
boolean g r e a t e r T h a n ( M y S t r i n g s);

}
/* nowa klasa - klasa adaptera */
class M y S t r i n g C o m p a r a t o r implements Comparator {

int c o m p a r e ( O b j e c t o l , O b j e c t o2) {
int r e s u l t ;
if ( ( ( S t r i n g ) o l ) . g r e a t e r T h a n ( o 2 ) ) {
result = 1
} else if ( ( ( S t r i n g ) o l ) . e q u a l s ( o 2 ) ) {
r e s u l t = 0;
} else (
result = -1;

}
8.4. Wybór wzorców projektowych i gotowych komponentów 373

return result;
}
}

Dziedziczenie i delegowanie we wzorcu projektowym Adapter

Wzorzec projektowy Adapter wykorzystuje dziedziczenie specyfikacyjne między kla-


sami Cl i e n t l n t e r f a c e - i Adapter. W celu realizowania operacji deklarowanych w interfejsie
Cl i ent Interface- klasa Adapter deleguje wywołania do klasy implementatora LegacyCl ass-.
Kod kliencki zawierający odwołania do interfejsu Cl i e n t l n t e r f a c e - współpracuje w ten
sposób z instancjami klasy Adapter- w sposób niejawny, bez konieczności dokonywania
w nim jakichkolwiek zmian. Zauważmy, że ta sama klasa Adapter- może być użyta do dowol-
nych podtypów klasy LegacyCl ass-.
Wzorce projektowe Most i Adapter podobne są do siebie pod względem przeznaczenia
i struktury: oba oddzielają interfejs od jego implementacji, oba też korzystają z dziedziczenia
i delegowania. Różnią się jednak pod względem kontekstu, w którym są stosowane, i kolej-
ności, w jakiej następują dziedziczenie i delegowanie: we wzorcu Adapter najpierw mamy do
czynienia z dziedziczeniem, później z delegowaniem, we wzorcu Most odwrotnie. Wzorzec
Adapter przydatny jest w sytuacji, gdy zarówno interfejs kliencki (Cl i ent I nterf ace-), jak i jego
implementacja (LegacyCl ass ) są ustalone i niezmienne, wzorzec Most znajduje natomiast
zastosowanie w sytuacji tworzenia nowego kodu, zapewniając większą rozszerzalność.

8.4.3. Hermetyzacja kontekstu


za pomocą wzorca projektowego Strategia
Wyobraźmy sobie aplikację mobilną, pracującą na przenośnym komputerze, którego używa
serwisant samochodowy. Aplikacja ta powinna być zdolna do korzystania z różnych proto-
kołów sieciowych, zależnie od ich dostępności — i tak na przykład w rodzimym warsztacie
serwisanta wspomniany komputer pracować ma w oparciu o połączenie bezprzewodowe
z lokalnym routerem, choć dla celów większej wydajności przy konfigurowaniu aplikacji
pożądana jest możliwość współpracy za pomocą połączenia kablowego (Ethernet) z tymże
routerem; gdy serwisant znajduje się w drodze, musi mieć możliwość łączności z serwerem
w swoim warsztacie za pośrednictwem mobilnych usług 3G, takich jak na przykład UMTS.
Oznacza to konieczność nie tylko uwzględnienia w aplikacji różnych protokołów siecio-
wych, lecz także możliwość dynamicznego, automatycznego przełączania się między nimi,
stosownie do lokalnych warunków technicznych (i ekonomicznych). Co więcej, poszerza-
nie aplikacji o możliwość współpracy z nowymi protokołami ma się odbywać bez koniecz-
ności jej rekompilowania.
Opisana sytuacja idealnie nadaje się do zastosowania kolejnego wzorca — wzorca pro-
jektowego Strategia (patrz sekcja A.9 i książka E. Gammy i innych [Gamma i in., 1994]).
Model wspomnianej aplikacji widoczny jest na rysunku 8.8, a na listingu 8.4 przedstawiamy
natomiast implementację jej Idas w języku Java. Klasie strategii (Strategy) odpowiada tu
abstrakcyjna klasa Networklnterface — wspólna superklasa dla wszelkich protokołów
374 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

sieciowych; kontekst (klasa Context ) realizowany jest przez obiekt NetworkConnection, repre-
zentujący połączenie „punkt-punkt" między komputerem przenośnym a zdalnym hostem.
Rolę klienta (Cl i ent-) pełni aplikacja pracująca na tym komputerze. Klasa ustalająca zasady
(Policy) reprezentowana jest tu przez klasę LocationManager, odpowiedzialną za moni-
torowanie dostępnych warunków połączenia w bieżącej lokalizacji i automatyczne wybieranie
odpowiedniej specjalizacji Networklnterface stosownie do tych warunków. Gdy obiekt klasy
LocationManager wywołuje metodę s e t N e t w o r k l n t e r f a c e Q , obiekt NetworkConnection
kończy pracę aktualnie wybranej implementacji i przełącza się na inną, w sposób przezro-
czysty dla reszty aplikacji.

Rysunek 8.8. Zastosowanie wzorca projektowego Strategia do hermetyzacji dynamicznego przełączania


interfejsu Networklnterface między różnymi jego implementacjami. Za przełączanie to, zgodnie z określo-
nymi zasadami, stosownie do warunków bieżącej lokalizacji odpowiedzialny jest obiekt LocationManager.
Aplikacja (Application) nie jest świadoma bieżącej implementacji interfejsu Networklnterface. Patrz
także kod na listingu 8.4

Listing 8.4. Zastosowanie wzorca projektowego Strategia do hermetyzacji wielu implementacji interfejsu
N e t w o r k l n t e r f a c e (w języku Java). Dla uproszczenia pominęliśmy między innymi obsługę sytuacji
wyjątkowych, patrz także diagram klas na rysunku 8.8

/** Obiekt klasy NetworkConnection reprezentuje połączenie używane przez klienta.


Odpowiada on obiektowi Context wzorca projektowego Strategia */
public class NetworkConnection (
private String destination;
private Networklnterface intf;
private StringBuffer queue;

public NetworkConnect(String destination, Networklnterface intf) {


this.destination = destination;
this.intf = intf;
this.intf.open(destination);
this.queue = new StringBufferQ ;
}
8.4. Wybór wzorców projektowych i gotowych komponentów 375

public void send(byte msg[]) { •


/ / kolejkowanie komunikatów przeznaczonych do wysiania jako środek
/ / radzenia sobie z tymczasowym brakiem połączenia
queue.concat(msg);
if (intf.isReady()) {
intf.send(queue);
queue.setLength(O);
}
}
public byte [] receiveQ {
return intf.receiveQ;
}
public void setNetworkInterface(NetworkInterface newlntf) {
intf.close();
newlntf.open(destination);
intf = newlntf;
}
}
/** Klasa LocationManager decyduje o wyborze konkretnej implementacji interfejsu
Networklnterface, stosownie do warunków technicznych i ekonomicznych danej
lokalizacji */
public class LocationManager {
private Networklnterface networklntf;
private NetworkConnection networkConn;

/ / Poniższa metoda wywoływana jest przez procedurę obsługi zdarzenia


/ / przy zmianie lokalizacji

public void doLocation() {


if (isEthernetAvai1able()) {
networklntf = new EthernetNetworkQ;
} else if (isWaveLANAvai 1 able()) {
networklntf = new WaveLANNetwork();
} else if (isUMTSAvailableO) {
networklntf = new UMTSNetworkQ;
} else {
networklntf = new QueueNetworkQ;
}
networkConn.setNetwork Interface(network Intf);
}
}

Dziedziczenie i delegowanie we wzorcu projektowym Strategia

W kontekście rysunków 8.6 i 8.8 wzorce Most i Strategia wydają się niemal identyczne.
Różnica między nimi kryje się w kreatorze klas implementujących interfejs: w ramach wzorca
Most klasy te (Concretelmpl ementati on-) tworzone są oraz inicjowane przez klasę Abstracti on-,
376 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

we wzorcu Strategia kontekst (Context-) jest natomiast niezależny od (i nieświadomy) kon-


kretnych implementacji (ConcreteStrategy) — to klient odpowiedzialny jest za ich tworzenie
i odpowiednie konfigurowanie kontekstu. Co więcej, we wzorcu Most konkretna implemen-
tacja tworzona jest i wiązana z interfejsem zwykle w czasie inicjowania aplikacji, podczas
gdy na gruncie wzorca Strategia konkretne strategie tworzone są i dynamicznie wybierane
w czasie działania programu.

8.4.4. Hermetyzacja platformy


za pomocą wzorca projektowego Fabryka abstrakcyjna
Jako kolejny przykład wyobraźmy sobie inteligentny dom, a konkretnie aplikację rejestrującą
sygnały z rozproszonych po domu czujników (żarówka włączona/wyłączona, okno otwarte/
zamknięte, temperatura wewnątrz i na zewnątrz, prognoza pogody) i uruchamiającą — na
podstawie predefiniowanych wzorców — mechanizmy wykonawcze (włączenie klimatyzacji,
aktualizację statystyki zużycia energii, zamknięcie drzwi garażu, włączenie alarmu przeciw-
włamaniowego). Mimo iż rozwiązania sprzętowe tej kategorii dostarczane są przez wielu
producentów (między innymi EIB czy Luxmate), możliwość współdziałania produktów po-
chodzących od różnych wytwórców jest raczej kiepska, co z jednej strony, skazuje klienta na
wybór wyposażenia w całości od jednego dostawcy, z drugiej, utrudnia tworzenie oprogra-
mowania uniwersalnego dla wszystkich producentów.
Wspomnianą niekompatybilność można jednak pogodzić przy użyciu wzorca projek-
towego Fabryka abstrakcyjna (patrz sekcja A.l i książka E. Gammy i innych [Gamma i in.,
1994]). Dla naszego inteligentnego domu wybrany producent dostarczy wszystkie kategorie
czujników — fotokomórki, czujniki przepalenia żarówek, czujniki temperatury i tym po-
dobne. W schemacie przedstawionym na rysunku 8.9 (ograniczyliśmy się do dwóch pierw-
szych kategorii czujników): klasy abstrakcyjne LightBulb (żarówka) i Blind (fotokomórka)
pełnią funkcje produktów abstrakcyjnych (AbstractProduct-), zaś konkretne realizacje tych
produktów reprezentowane są przez klasy charakterystyczne dla każdej pary „produkt-
producent" (EIBLi ghtBul b, LuxmateLi ghtBul b, EIBBlind, LuxmateBl i nd).. Klasy repre-
zentujące fabryki, po jednej dla każdego producenta (LuxmateFactory, EIBFactory), sta-
nowiące konkretyzacje fabryki abstrakcyjnej (AbstractFactory-, tu reprezentowanej przez
HouseFoctory), dostarczają metody „wytwarzające" konkretne kategorie produktów (create
^•LightBulbQ, createBl ind()). Klasa kliencka (Thef tAppl i cati on) ma dostęp jedynie do
interfejsów AbstractFactory i AbstractProduct, nie jest więc świadoma pochodzenia pro-
duktów od konkretnych dostawców.

Dziedziczenie i delegowanie we wzorcu projektowym Fabryka abstrakcyjna

Wzorzec projektowy Fabryka abstrakcyjna do rozdzielania interfejsu z jego realizacją


wykorzystuje dziedziczenie specyfikacyjne. Ponieważ produkty różnych producentów nie
mogą być dowolnie mieszane ze sobą: przykładowo w tym samym systemie domu inteligentnego
nie jest możliwe równoczesne wykorzystywanie produktów EIBBlind i LuxmateBulb czy
produktów EIBBul b i LuxmateBl i nd. Klient, by zapewnić sobie spójność marki otrzymywanych
produktów (czyli odpowiedniość typów produkowanych obiektów), deleguje ich tworzenie
8.4. Wybór wzorców projektowych i gotowych komponentów 377

Rysunek 8.9. Zastosowanie wzorca projektowego Fabryka abstrakcyjna do zapewnienia współdzia-


łania różnych platform tworzących infrastrukturę inteligentnego domu. Zależności między klasami
odzwierciedlone są przez relacje «cal 1»

do fabryki, reprezentowanej przez abstrakcyjny interfejs (tu HouseFactory); nie jest on przy
tym świadom konkretnej fabryki (EIBFactory albo LuxmateFactory) implementującej ów
interfejs. Ten właśnie szczegół ułatwia przystosowywanie aplikacji do specyfiki różnych
producentów — wystarczy zmienić implementację interfejsu HouseFactory, bez ingerowania
w resztę aplikacji.

8.4.5. Hermetyzacja przepływu sterowania


za pomocą wzorca projektowego Polecenie
W systemach interaktywnych i systemach transakcyjnych często wymagane są funkcje wy-
konywania, cofania i rejestrowania żądań użytkownika, bez wnikania w treść tych żądań. Przy-
kładowo w podsystemie zarządzania turniejami systemu ARENA (TournamentManagement)
chcielibyśmy w każdym z meczów rejestrować kolejne ruchy graczy, by później odtwarzać
je na żądanie kibiców. Ponieważ zakładamy przydatność systemu dla szerokiej gamy gier
i wynikającą stąd jego niezależność od konkretnej gry, nie chcemy, by klasa odpowiedzial-
na za nagrywanie historii meczu uzależniona była od konkretnej gry, czyli od konkretnego
znaczenia pojęcia „ruch".
Dla osiągnięcia tego efektu możemy użyć wzorca projektowego Polecenie (patrz sekcja
A.4 oraz książka E. Gammy i innych [Gamma i in., 1994]). Kluczem do odseparowania im-
plementacji ruchów dla konkretnych gier od zarządzania samymi ruchami jako takimi jest
reprezentowanie tych ruchów jako obiektów poleceń, wywodzących się z klasy abstrakcyjnej
378 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

Command• (na rysunku 8.10 jest to klasa Move). Klasa ta deklaruje operacje wykonywania, cofania
i rejestrowania poleceń, podczas gdy każda z klas ConcreteCommand- (czyli na przykład klasy
Ti cTacToeMove i ChessMove) implementuje specyfikę tych czynności dla konkretnej gry. Za-
pewnienie zarządzania ruchami dla nowo dodanej gry sprowadza się do zaimplementowania
stosownej subklasy klasy Move.

Rysunek 8.10. Zastosowanie wzorca projektowego Polecenie do rejestrowania historii meczów (Match)
w systemie ARENA

Dziedziczenie i delegowanie we wzorcu projektowym Polecenie

Wzorzec projektowy Polecenie wykorzystuje dziedziczenie specyfikacyjne między


klasami Command• i ConcreteCommand-, co umożliwia dodawanie nowych poleceń, niezależnie
od klasy wywołującej (Invoker, tutaj klasa Match). Delegowanie odbywa się w dwóch miej-
scach: z klasy ConcreteCommand- do klasy Receiver- (tu GameBoard) oraz z klasy Invoker- do
klasy Command-, co umożliwia dynamiczne tworzenie obiektów ConcreteCommand- oraz wyko-
nywanie i rejestrowanie reprezentowanych przez nie poleceń. Wzorzec projektowy Polecenie
wykorzystywany jest często w architekturze „model-widok-kontroler" (MVC): obiekty klasy
Receiver są obiektami modelu, obiekty klas Invoker i Command-— obiektami kontrolera, zaś
widoki są klientami (Cl i ent-) wydającymi polecenia.

8.4.6. Hermetyzacja hierarchii


za pomocą wzorca projektowego Kompozyt
Narzędzia dedykowane budowaniu interfejsów użytkownika, między innymi Swing i Cocoa,
dostarczają programiście wiele „cegiełek", czyli specjalizowanych klas przeznaczonych do te-
go celu. Każda z tych klas prezentuje określone zachowanie — obsługę wprowadzanego tekstu,
obsługę „naciskania" przycisków, rozwijanie menu i tym podobne. W ramach projektu inter-
fejsu użytkownika poszczególne jego elementy mogą być łączone (agregowane) w okna w celu
tworzenia interfejsów specyficznych dla danej aplikacji, przykładowo okno dialogowe konfi-
gurowania preferencji zawiera zazwyczaj zbiór niezależnych pól wyboru (check box) odpo-
wiadających poszczególnym cechom aplikacji.
8.4. Wybór wzorców projektowych i gotowych komponentów 379

W miarę jak okna te stają się coraz bardziej skomplikowane i obejmują coraz więcej
kontrolek, zarządzanie nimi (przesuwanie, zmiana rozmiarów) w sposób zapewniający za-
chowanie układu i wyglądu stwarza coraz większe ryzyko wymknięcia się spod kontroli.
W związku z tym, nowoczesne narzędzia opisywanej kategorii umożliwiają programiście orga-
nizowanie interfejsu użytkownika w sposób hierarchiczny: wybrane kontrolki grupowane
zostają w panele, które same stają się złożonymi kontrolkami, bowiem każdym panelem zarządza
się tak samo jak „zwykłą" kontrolką. W naszym przykładowym oknie wyboru preferencji
(patrz rysunek 8.11) można wyodrębnić trzy takie panele: górny — obejmujący tytuł i krótką
instrukcję dla użytkownika, środkowy — zawierający kontrolki wyboru i opatrujące je etykiety
oraz dolny — zawierający przyciski OK i Cancel. Każdy panel odpowiedzialny jest za zawarte
w nim kontrolki i „podpanele", całe okno dialogowe ma więc do czynienia jedynie z trzema
panelami zamiast z kilkunastoma niezależnymi kontrolkami.

Rysunek 8.11. Anatomia okna wyboru preferencji. Agregaty zwane panelami grupują kontrolki w celu
ich skonsolidowanego zachowania się przy przemieszczaniu i zmianie rozmiaru okna

Hierarchię klas wynikającą z opisanego grupowania przedstawiliśmy na diagramie obiek-


tów widocznym na rysunku 8.12.
Swing realizuje opisaną koncepcję za pomocą wzorca projektowego Kompozyt (patrz
sekcja A.5 oraz książka E. Gammy i innych [Gamma i in., 1994]). Zastosowanie tego wzorca
do przykładowego interfejsu użytkownika pokazane jest na rysunku 8.13. Abstrakcyjna klasa
Component jest superklasą dla wszystkich kontrolek: pól wyboru (Checkbox), przycisków
(Button) czy etykiet (Label). Composite, jako jedna z subklas klasy Component, jest specjal-
nym obiektem reprezentującym agregat, na przykład wspomniany wcześniej panel. Zauważ-
my, że klasa ta specjalizuje się w postaci dwóch subklas: Window, reprezentującej okno, oraz
Panel, stanowiącej szczyt hierarchii agregatów; specjalizacja ta wiąże się z wyposażeniem
obu klas w mechanizmy współpracy z przeglądarką (Wi ndow) i menedżerem okien (Panel).

8.4.7. Heurystyki wyboru wzorców projektowych


Wybór właściwego wzorca projektowego dla rozwiązywania konkretnego problemu jest trud-
nym zadaniem, szczególnie dla programisty nieposiadającego doświadczenia w tej dziedzinie.
Katalog znanych wzorców projektowych jest obszerny i wciąż ewoluuje, nie sposób więc
wymagać od jakiegokolwiek programisty kompletnej jego znajomości. Ponieważ wzorce
380 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

Rysunek 8.12. Hierarchia obiektów interfejsu przedstawionego na rysunku 8.11

Rysunek 8.13. Zastosowanie wzorca projektowego Kompozyt do organizacji widżetów środowiska


Swing. „Korzeniem" hierarchii tego środowiska jest klasa Composite, zaś jej „liśćmi" — pojedyncze
kontrolki (Checkbox, Button, Label i tym podobne). Węzły pośrednie tej hierarchii reprezentują agre-
gaty (Panel, Window). Przesuwanie lub zmiana rozmiarów panelu ma wpływ na wszystkie zawarte w nim
kontrolki

p r o j e k t o w e p o z o s t a j ą w z w i ą z k u z k o n k r e t n y m i celami p r o j e k t o w y m i i w y m a g a n i a m i p o z a -
f u n k c y j n y m i , m o ż n a p o k u s i ć się o i d e n t y f i k o w a n i e w z o r c ó w p r o j e k t o w y c h n a p o d s t a w i e
o k r e ś l o n y c h fraz języka n a t u r a l n e g o w y s t ę p u j ą c y c h w d o k u m e n t a c h analizy w y m a g a ń ( R A D )
8.4. Wybór wzorców projektowych i gotowych komponentów 381

i projektu systemu (SDD), zgodnie z heurystykami analogicznymi do heurystyk Abbotta, opi-


sywanych w rozdziale 5. „Analiza wymagań". Przykłady wspomnianych takich heurystyk dla
wzorców projektowych opisywanych w tym rozdziale przedstawiamy w tabeli 8.3.

Heurystyki identyfikowania wzorców projektowych


na podstawie fraz języka naturalnego
Wzorce projektowe dedykowane są konkretnym celom projektowym i wymaganiom pozafunkcyjnym.
Podobnie jak w przypadku heurystyk Abbotta, pewne frazy stanowią bardzo prawdopodobne kan-
dydatury na użycie wzorców projektowych, zgodnie z tabelą 8.3.

Tabela 8.3. Heurystyczne reguły identyfikowania fraz języka naturalnego jako kandydatur na stoso-
wanie wzorców projektowych

Prawdopodobny
Fraza
wzorzec projektowy
„niezależność od dostawcy" Fabryka abstrakcyjna
„niezależność od platformy"

„musi być zgodny z istniejącym interfejsem" Adapter


„musi współpracować z istniejącym komponentem"

„musi mieć możliwość rozszerzania o nowe protokoły" Most

„wszystkie polecenia muszą mieć możliwość wycofywania" Polecenie


„wszystkie transakcje muszą być rejestrowane"

„musi obsługiwać zagregowane struktury" Kompozyt


„musi dopuszczać hierarchie o dowolnej głębokości i szerokości"

„zasady muszą być oddzielone od realizujących je mechanizmów" Strategia


„musi umożliwiać dynamiczną wymianę algorytmów podczas
wykonywania programu"

8.4.8. Identyfikowanie i przystosowywanie frameworków aplikacyjnych

Frameworki aplikacyjne

Frameworkiem aplikacyjnym nazywamy częściową aplikację, przeznaczoną do wielo-


krotnego wykorzystywania, możliwą do przystosowywania potrzeb związanych z tworzeniem
specjalizowanych aplikacji [Johnson i Foote, 1988], W odróżnieniu od bibliotek klas, fra-
meworki dedykowane są konkretnym technologiom, takim jak przetwarzanie danych ekspe-
rymentalnych czy telefonia komórkowa, lub konkretnym dziedzinom aplikacyjnym, jak
interfejsy użytkownika czy kontrola lotów w czasie rzeczywistym. Wykorzystywanie frame-
worków daje wyraźne korzyści w postaci wielokrotnego wykorzystywania tych samych roz-
wiązań i rozszerzalności. Możliwość wielokrotnego stosowania gotowych rozwiązań stanowi
owoc zarówno doświadczeń zebranych w związku z dziedziną aplikacyjną, jak wysiłku pro-
gramistów zmierzających do uwolnienia siebie (i kolegów z branży) od konieczności wie-
lokrotnego rozwiązywania tych samych problemów ab ovo. Rozszerzalność frameworków
382 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

realizowana jest za pomocą tak zwanych metod zahaczanych (hook methodsf, które mogą być
nadpisywane w docelowych aplikacjach. Metody zahaczane systematycznie oddzielają inter-
fejsy od konkretnego zachowania, specyficznego dla danej dziedziny aplikacyjnej, co daje
możliwość szybkiego implementowania nowych cech i usług.
Frameworki, pod względem usytuowania w ogólnym procesie tworzenia aplikacji, po-
dzielić można na trzy następujące kategorie:

• Frameworki infrastrukturalne przeznaczone są do upraszczania samego procesu


tworzenia aplikacji i bardzo rzadko dostarczane są klientowi końcowemu (który
może nawet nie być świadom ich istnienia). Związane są między innymi z systemami
operacyjnymi opisanymi przez R. H. Campbella i N. Islacna [Campbell i Islam, 1993],
debuggerami omówionymi w książce B. Brueggego, T. Gottschalka i B. Luo [Bruegge
i in., 1993], zadaniami komunikacyjnymi, które opisane zostały w pracy D. C.
Schmidta [Schmidt, 1997], i projektowaniem interfejsów użytkownika omówionym
przez A. Weinanda, E. Gammę i R. Martyego [Weinand i in„ 1988], Znanym przy-
kładem frameworku tej kategorii jest Java Swing [JFC, 2009],
• Frameworki pośredniczące wykorzystywane są do integrowania istniejących,
rozproszonych aplikacji i komponentów. Wśród najbardziej znanych reprezentantów
tej kategorii wymienić należy MFC i DCOM firmy Microsoft, Java WebObject opisany
przez G. Wilsona i J. Ostrema [Wilson i Ostrem, 1999], WebSphere [IBM], WebLogic
Enterprise Application [BEA], implementacje architektury CORBA [OMG, 2008]
i transakcyjne bazy danych.
• Frameworki aplikacyjne związane są ze specyficznymi zastosowaniami informatyki
w takich obszarach jak telekomunikacja, lotnictwo, modelowanie środowiska
naturalnego, zarządzanie liniami produkcyjnymi, inżynieria finansowa omówiona
w książce E. T. Birrera [Birrer, 1993] czy modelowanie procesów biznesowych
[JavaEE, 2009],

Dwie pierwsze kategorie frameworków mają istotne znaczenie dla (ogólnie pojmowa-
nego) szybkiego tworzenia systemów wysokiej jakości, lecz zwykle nie mają żadnego znaczenia
z punktu widzenia użytkowników tych systemów. Frameworki aplikacyjne stanowią na-
tomiast istotne wsparcie dla tworzenia aplikacji użytkowych. W rezultacie zakup gotowych
rozwiązań dwóch pierwszych kategorii okazuje się wyraźnie bardziej opłacalny niż ich samo-
dzielne tworzenie, o czym piszą M. E. Fayad i D. S. Hamu [Fayad i Hamu, 1997].
Frameworki, ze względu na możliwy sposób ich użytkowania, można także podzielić na
dwie następujące grupy:

• Frameworki białoskrzynkowe — ich zastosowanie sprowadza się do możliwości


wykorzystywania dziedziczenia po oferowanych klasach oraz ich dynamicznego
wiązania, czyli do definiowania Idas pochodnych i nadpisywania predefiniowanych
metod zahaczanych, zgodnie ze zdefiniowanymi wzorcami, co omawiają E. Gamma
i inni [Gamma i in., 1994],

5
Zwanych także „punktami zaczepienia" — przyp- tłum.
8.4. Wybór wzorców projektowych i gotowych komponentów 383

• Frameworki czarnoskrzynkowe — udostępniają one interfejsy dla komponentów,


przeznaczonych do integrowania („podłączania") z aplikacją docelową. Gotowa
funkcjonalność wykorzystywana jest poprzez definiowanie komponentów zgodnych
ze wspomnianymi interfejsami, zaś korzystanie z tych komponentów odbywa się
poprzez delegowanie odwołań.

Korzystanie z frameworków białoskrzynkowych wymaga dogłębnej znajomości ich


struktury wewnętrznej. Docelowa aplikacja staje się silnie związana z detalami odnośnego
frameworku, przez co dokonywane wewnątrz niego zmiany wymagają zwykle ponownego
kompilowania tej aplikacji. Frameworki czarnoskrzynkowe są łatwiejsze w użytkowaniu, któ-
rego podstawą jest delegowanie wywołań zamiast dziedziczenia klas. Stanowią one jednak
poważniejsze wyzwanie dla swych twórców, którzy definiując interfejsy i metody zahaczane,
muszą przewidywać możliwie jak najpełniejsze spektrum ich przyszłego użycia. Ponieważ
jednak bazują na dynamicznych zależnościach między obiektami, a nie na statycznych zależ-
nościach między klasami, są łatwiejsze w rozszerzaniu i dynamicznej rekonfiguracji, o czym
piszą R. Johnson i B. Foote [Johnson i Foote,1988].

Frameworki, biblioteki klas i wzorce projektowe

Frameworki są ściśle powiązane ze wzorcami projektowymi, bibliotekami klas i kom-


ponentami.
W przeciwieństwie do wzorców projektowych, frameworki koncentrują się na kon-
kretnych projektach, algorytmach i ich implementacjach w konkretnym języku (językach)
programowania, podczas gdy wzorce projektowe reprezentują abstrakcyjne koncepcje i obej-
mują niewielkie kolekcje współdziałających klas. Frameworki koncentrują się na konkretnych
dziedzinach aplikacyjnych, podczas gdy wzorce projektowe mogą być traktowane raczej jako
cegiełki do budowania frameworków.
Biblioteki klas tym różnią się od frameworków, że są mniej specyficzne z punktu wi-
dzenia konkretnego zastosowania i inny jest charakter ich wykorzystywania: składniki takich
bibliotek, dedykowane (przykładowo) przetwarzaniu napisów (łańcuchów), arytmetyce liczb
zespolonych, tablicom czy zbiorom bitowym 6 nie odnoszą się bezpośrednio do konkretnych
zastosowań, dostarczają jedynie niskopoziomowe mechanizmy pomocne w tych zastosowa-
niach. Biblioteki Idas są konstrukcjami biernymi — w tym sensie, że nie realizują ograniczeń
ani nie determinują przepływu sterowania, w przeciwieństwie do aktywnych ze swej natury
frameworków. W praktyce biblioteki klas są zarówno używane do tworzenia samych frame-
worków, jak i wykorzystywane na równi z frameworkami do budowania aplikacji docelowych,
w których służą do implementowania odwołań zwrotnych (callbacks) w postaci specyficznego
dla tych aplikacji kodu realizującego obsługę zdarzeń na poziomie przetwarzania łańcuchów,
zarządzania plikami czy obliczeń numerycznych.

6
Zbiór bitowy to reprezentacja podzbioru pewnego wzorcowego zbioru X, w której każdego ele-
mentowi zbioru X odpowiada jeden bit; wartość 1 tego bitu oznacza przynależność elementu do
podzbioru. Reprezentacja taka stosowana jest powszechnie w różnych odmianach języka Pascal do
implementowania zmiennych typu Set ot T, które są podzbiorami zbioru zawierającego wszystkie
wartości typu T — przyp. tłum.
384 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

Komponenty są samoistnymi instancjami klas, łączonymi w formę kompletnej aplikacji;


w kategoriach wielokrotnego wykorzystywania komponent jest czarną skrzynką definiują-
cą spoisty zbiór operacji określanych przez składnię i semantykę interfejsu. W porównaniu
z frameworkami komponenty mają relatywnie mniejszy stopień spoistości i mogą być wy-
korzystywane nawet na poziomie kodu binarnego — aplikacja może z nich korzystać bezpo-
średnio, bez konieczności definiowania subklas, niekoniecznie musi też być rekompilowana,
gdy zmienia się implementacja samego komponentu. Wzajemna relacja między komponen-
tami a frameworkami nie jest ściśle zdeterminowana: z jednej strony, frameworki mogą być
używane do tworzenia komponentów, których interfejs stanowi fasadę (patrz sekcja A.6) dla
wewnętrznej struktury klas frameworku; z drugiej jednak strony, komponenty mogą służyć
do budowania frameworków czarnoskrzynkowych. Ogólnie jednak rzecz biorąc, frameworki
wykorzystywane są w celu uproszczenia tworzenia infrastruktury i oprogramowania pośred-
niego (middleware), podczas gdy komponenty upraszczają opracowywanie konkretnych apli-
kacji użytkowych.

Przykład frameworku

WebObjects to zbiór frameworków przeznaczonych do tworzenia interaktywnych apli-


kacji webowych wykorzystujących dane przechowywane w relacyjnych bazach danych. Ów
„zbiór" to właściwie dwa frameworki infrastrukturalne: pierwszy, główny, o nazwie WebObjects,
dedykowany jest zarządzaniu interakcją między przeglądarką a serwerem W W W ; drugi
— Enterpri se Object Framework, w skrócie EOF — pełni funkcję adaptera umożliwiającego
łączenie się aplikacji z bazą danych konkretnego producenta. Obecnie EOF dostarcza adaptery
dla serwerów Informix, Oracle i Sybase oraz interfejsu ODBC. Bardziej szczegółowe informacje
na temat EOF znajdą czytelnicy w książce G. Wilsona i J. Ostrema [Wilson i Ostrem, 1999].
Na rysunku 8.14 widoczny jest przykład realizacji dynamicznej witryny W W W zbudo-
wanej przy użyciu WebObjects. Wszystko zaczyna się od wysłania przez przeglądarkę W W W
(WebBrowser) żądania do serwera W W W (Webserver). Serwer W W W analizuje treść żądania
i jeżeli stwierdzi, że dotyczy ono statycznej strony W W W , przekazuje je do obiektu Stati cHTML,
który pobiera żądaną stronę i odsyła ją jako odpowiedź do przeglądarki wyświetlającej otrzy-
maną stronę użytkownikowi. Jeśli żądanie określa dynamiczną stronę W W W , przekazywane
jest do obiektu WOAdaptor, który po wstępnym przetworzeniu przekazuje je do komponentu
WebObjectsAppl i c a t i on. Na podstawie szablonów (Tempi a t e ) zdefiniowanych przez pro-
gramistę i odpowiednich danych otrzymanych z bazy ( R e i a t i o n a l D a t a b a s e ) komponent
WebObjectsApplication generuje wynikową stronę W W W , która za pośrednictwem kom-
ponentu WOAdaptor odsyłana jest z powrotem do serwera Webserver. Serwer odsyła otrzymaną
stronę do przeglądarki, która sformułowała żądanie.
Kluczową abstrakcją udostępnianą przez WebObjects jest rozszerzenie protokołu HTTP
o funkcję zarządzania stanem. Jak wiadomo, protokół HTTP jest z definicji protokołem bez-
stanowym; dla każdego żądania generowana jest odpowiedź i nic ponadto — nie jest utrzy-
mywana żadna informacja dotycząca powiązań między poszczególnymi żądaniami. Informacja
taka wymagana jest jednak przez większość aplikacji webowych, na przykład w systemie ARENA
trudno wymagać od gracza, by identyfikował się przed wykonaniem każdego ruchu; co więcej,
gracz musi mieć możliwość kontynuowania meczu nawet w przypadku zamknięcia i ponownego
8.4. Wybór wzorców projektowych i gotowych komponentów 385

Rysunek 8.14. Przykład realizacji dynamicznej witryny W W W przy użyciu WebObjects

uruchomienia przeglądarki. Aplikacje webowe wypełniają tę lukę za pomocą różnorodnych


środków, między innymi cookies, dynamicznie generowanych URL-i oraz ukrytych pól w for-
mularzach HTML. WebObjects zdolny jest wyręczyć je w tym dziele, oferując mechanizm
przedstawiony na rysunku 8.15.

Rysunek 8.15. Klasy frameworku WebObjects odpowiedzialne za zarządzanie informacją o stanie


żądań (State Management Classes)

Obiekt WOAppl i c a t i o n reprezentuje aplikację uruchomioną na serwerze W W W


(Webserver), oczekującą na żądania ze zdalnej przeglądarki (WebBrowser). Cykl sekwencji
„żądanie-odpowiedż" rozpoczyna się w momencie, gdy WOAdaptor odbiera żądanie HTTP.
Po spakowaniu do postaci obiektu WORequest żądanie to przekazywane jest do obiektu
WOAppl i cati on. Za śledzenie stanu poszczególnych sesji odpowiedzialna jest klasa WOSessi on,
umożliwiająca śledzenie oddzielnie każdego dialogu z użytkownikiem, nawet w ramach tej
samej instancji aplikacji. Obiekt klasy WOSessi on powiązany jest z jednym lub kilkoma obiek-
tami klasy WOComponent, reprezentującymi powtarzalne strony W W W (lub ich fragmenty)
386 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

wyświetlane w ramach poszczególnych sesji. Obiekty WOComponents mogą zawierać elementy


dynamiczne (Dynami cElement), odpowiedzialne za reprezentowanie treści pobieranych z bazy
danych. W ten oto sposób tworzona jest dynamicznie informacja o stanie sesji; obiekt WOSessi on
przechowuje ją na serwerze, korzystając z usług obiektu WOSessi onStore, i odtwarza przy każ-
dym żądaniu wchodzącym w skład tej właśnie sesji.
Proces tworzenia aplikacji webowej przy użyciu WebObjects sprowadza się zasadniczo
do odpowiedniego precyzowania klas WOAppl i cati on, WOSessi on i WOComponent oraz prze-
chwytywania żądań przepływających między nimi. Precyzowanie to rozpoczyna się od mo-
mentu tworzenia obiektu WOAppl i cati on (poprzez odpowiedni konstruktor), a kończy w chwili
kończenia jego pracy (przez przedefiniowanie metody terminate()) i — oczywiście — prze-
biega przez szereg etapów pośrednich specyficznych dla aplikacji docelowej.

8.5. Zarządzanie wykorzystywaniem gotowych rozwiązań


Historycznie rzecz biorąc, tworzenie oprogramowania rozwijało się jako rzemiosło, a każda
aplikacja tworzona była zgodnie z wymaganiami pojedynczego klienta, w sposób określony
przez reguły pragmatyki właściwej konkretnej manufakturze. Koszt oprogramowania był
niewielki w porównaniu z kosztem sprzętu komputerowego 7 , wobec czego same komputery
i ich zastosowania były rarytasem, na który stać było tylko nielicznych. Czasy się zmieniły,
osiągnięcia technologiczne i reguły rynku wymusiły drastyczny spadek cen sprzętu, kom-
putery stały się urządzeniami elektroniki użytkowej, na równi z telewizorami. Rosnące wy-
kładniczo z czasem możliwości tanich komputerów znacznie zwiększyły zarówno popyt na
ich zastosowania, jak i potencjalne możliwości tych zastosowań. Od komputerów wymaga
się coraz więcej (czemu starają się sprostać coraz szybsze procesory, coraz pojemniejsze dyski
i tym podobne) znacznie bardziej skomplikowanych rzeczy — co znajduje swój wyraz w ro-
snącej złożoności tworzonego oprogramowania i zwiększających się kosztach jego wytwa-
rzania. Tendencje te osiągnęły już punkt krytyczny, co zmusiło menedżerów do poszukiwania
możliwości zarówno w zakresie zmniejszania kosztów tworzenia systemów informatycznych,
jak i pod względem sposobów ułatwiających radzenie sobie z ich złożonością. Wobec braku
cudownego panaceum, w tym względzie sprawdzonymi i skutecznymi metodami osiągania
tych celów okazały się zabiegi zmierzające do jak największego wykorzystywania istniejących
(gotowych) rozwiązań. Wielokrotne używanie — wzorców projektowych, frameworków i kom-
ponentów — niesie ze sobą liczne korzyści zarówno w aspekcie technicznym, jak i menedżer-
skim. Oto one.

• Mniejszy wysiłek programistów. Używając gotowych rozwiązań lub komponentów,


oszczędzamy sobie wysiłku związanego z rozwiązywaniem standardowych proble-
mów; co więcej, system budowany w zgodzie z wzorcami projektowymi daje się
łatwiej rozbudowywać i jest bardziej elastyczny pod względem typowych zmian.
Programiści mogą zainwestować swój wysiłek w sposób bardziej produktywny,
między innymi w lepsze testowanie zmierzające do uzyskania lepszej jakości.

7
Co więcej, wobec niebotycznych cen sprzętu, liczonych często w milionach dolarów, żądanie dodat-
kowych pieniędzy za oprogramowanie wydawało się klientowi wręcz niemoralne — przyp. tłum.
8.5. Zarządzanie wykorzystywaniem gotowych rozwiązań 387

• Mniejsze ryzyko. Używając wielokrotnie tych samych wzorców i (lub) komponentów,


lepiej potrafimy przewidywać przyszłe problemy. Ponadto łatwiej możemy szacować
czasochłonność integrowania komponentów i adaptowania wzorców projektowych
do konkretnych potrzeb, co w rezultacie daje proces bardziej przewidywalny i obar-
czony mniejszym ryzykiem.
• Upowszechnienie standardowej terminologii. Konsekwentne wykorzystywanie wzor-
ców projektowych i komponentów ze standardowego zbioru stanowi zachętę do po-
sługiwania się standardowym słownictwem. Określenia, takie jak Adapter, Most,
Polecenie czy Fasada, niosą ze sobą precyzyjne znaczenie dotyczące koncepcji, które
powinny być znane każdemu programiście. Zmniejsza to dowolność terminologiczną
i zarazem ryzyko nieporozumień przy rozwiązywaniu powszechnych problemów
projektowych.
• Większa niezawodność. Wielokrotne wykorzystywanie gotowych rozwiązań samo
z siebie nie daje gwarancji niezawodności ani też nie umniejsza potrzeby testowania,
czego dowodem może być historia z rakietami Ariane 501 opisywana na początku
sekcji 3.1. Jednakże wzorce i komponenty wykorzystywane w jednym kontekście mogą
obnażyć usterki i uczynić ewidentnymi problemy, które mogłyby pojawić się w innym
kontekście. Ponadto opisywane wcześniej przesłanki — lepsze testowanie podsystemu,
dzięki oszczędności czasu uzyskanej przy jego tworzeniu, lepsze prognozowanie pro-
blemów i promowanie standardowej terminologii — przekładają się w oczywisty
sposób na większą niezawodność tworzonego oprogramowania.

Niestety, perspektywa korzystania z gotowych rozwiązań nie zawsze traktowana jest


entuzjastycznie w firmach programistycznych, za sprawą wielu przesłanek; oto najczęściej
spotykane.

• Syndrom „to nie nasz wynalazek" ( N I H — Not Invented Here). Jako że e d u k a c j a


informatyczna koncentruje się (dotychczas) raczej na tworzeniu nowych rozwiązań
niż na korzystaniu z istniejących, propozycje korzystania z gotowych rozwiązań
traktowane są przez programistów nieufnie, zwłaszcza gdy wiążą się z pewnymi ogra-
niczeniami. Programiści są wówczas przekonani, że samodzielnie potrafią wypro-
dukować rozwiązanie znacznie lepiej przystosowane do rozwiązania konkretnego
problemu (co przeważnie jest prawdą) i to w czasie krótszym od tego, jaki po-
trzebny byłby na konfigurowanie gotowego rozwiązania (co zwykle jest mrzonką).
Co gorsza, korzyści ze stosowania gotowych rozwiązań widoczne są dopiero w dłuż-
szej perspektywie, podczas gdy splendor (i inne korzyści) wynikający z samo-
dzielnego opracowania wspaniałego kodu daje natychmiastową satysfakcję.
• Nikłe wsparcie dla procesów. Procesy związane z identyfikowaniem, wykorzysty-
waniem i przystosowywaniem istniejących rozwiązań mają charakter inny niż te
związane z tworzeniem rozwiązań całkowicie nowych. Aktywności pierwszej grupy
to przede wszystkim odnajdywanie i skrupulatne selekcjonowanie dostępnych tech-
nologii i produktów, a także ich wszechstronne ocenianie i wzbogacanie własnych
doświadczeń w tej materii. Druga grupa aktywności wymaga natomiast kreatyw-
ności i doskonałego rozumienia problemu. Znakomita większość narzędzi i metod
programistycznych bardzo dobrze przystosowana jest do owej drugiej grupy, brakuje
388 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

natomiast tendencji promujących wielokrotne wykorzystywanie efektów raz wyko-


nanej pracy. Przykładowo istnieje wiele katalogów wzorców projektowych, trudno
jednak znaleźć jakąś systematyczną metodę, która pozwalałaby programistom nowi-
cjuszom na szybkie identyfikowanie wzorca najbardziej użytecznego w rozwiązy-
waniu określonego problemu.
• Szkolenie i trening. Wobec braku stosownych narzędzi wspierającym wielokrotne
wykorzystywanie gotowych rozwiązań, jedyną bodaj drogą budowania kultury pro-
gramistycznej w tym względzie pozostają szkolenia i treningi. Nie trzeba dodawać,
że ciężar ich organizowania spada na barki firm zatrudniających programistów.

W następnych sekcjach przyjrzymy się dokumentowaniu wykorzystywania gotowych


rozwiązań, jak również ustanawianiu ról związanych z rozwiązywaniem opisanych powyżej
problemów.

8.5.1. Dokumentowanie wykorzystywania gotowych rozwiązań


Wykorzystywanie gotowych rozwiązań wymaga każdorazowo działań dokumentacyjnych
dwojakiego rodzaju: dokumentowania zastosowanego rozwiązania wzorcowego oraz doku-
mentowania systemu, na potrzeby którego rozwiązanie to zostaje użyte.
Dokumentacja powtarzalnego rozwiązania (wzorca projektowego, frameworku, kom-
ponentu) powinna obejmować nie tylko opis samego rozwiązania, lecz także wskazanie pro-
blemów, które rozwiązanie to potencjalnie zdolne jest rozwiązywać, kompromisów, jakie
prawdopodobnie rozstrzygać będzie musiał w związku z tym programista, jak również propozy-
cje rozwiązań alternatywnych, no i — oczywiście — przykłady typowych zastosowań. Doku-
mentacje taką tworzy się jednak bardzo trudno, bowiem jej autor nie jest w stanie przewidzieć
wszystkich przyszłych problemów związanych z jej wykorzystywaniem. Ponadto dokumentacja
taka z konieczności jest raczej generyczna, abstrakcyjna i początkujący programiści mogą ją
zrozumieć jedynie na podstawie reprezentatywnych przykładów. Dokumentacja ta jest więc
daleka od ideału, niemniej jednak można łagodzić jej niedostatki przez sukcesywne uzupeł-
nianie przez samych programistów wykorzystujących opisywane rozwiązanie. Wśród ele-
mentów kwalifikujących się na takie uzupełnienia wymienić można między innymi:

• Odwołania do systemów używających przedmiotowego rozwiązania, czyli p r z y n a j m n i e j


zestawienie takich systemów. Jeśli bowiem we wspomnianym rozwiązaniu odkryty
zostanie błąd, oznaczać będzie potencjalne zagrożenia dla każdego z tych systemów.
• Przykłady zastosowań. Przykłady są najbardziej skutecznym mechanizmem ułatwia-
jącym rozumienie skomplikowanych koncepcji i mechanizmów, czyli również zalet
i wad konkretnego rozwiązania. Każdy przykład powinien zawierać opis problemu,
w związku z którym programiści sięgnęli po odnośne rozwiązanie.
• Rozważane rozwiązania alternatywne. Jak mieliśmy okazję przekonać się w tym roz-
dziale, wiele wzorców projektowych jest bardzo podobnych do siebie. Stwarza to
ryzyko wyboru wzorca niewłaściwego dla danego problemu, który to wybór może
powodować problemy znacznie większe niż opracowywanie własnego rozwiązania.
Programiści powinni więc udokumentować swój wybór, wskazując przesłanki, dla
których zrezygnowali z takiego czy innego rozwiązania alternatywnego.
8.5. Zarządzanie wykorzystywaniem gotowych rozwiązań 389

• Rozstrzygane kompromisy. Używanie gotowych rozwiązań, zwłaszcza wybór właści-


wego frameworku czy komponentu, wiąże się często z koniecznością godzenia sprzecz-
nych wymagań. Przykładowo z dwóch komponentów do wyboru jeden oferować może
wspaniale rozszerzalny interfejs i jednocześnie kiepską wydajność, drugi natomiast
może być niesamowicie wydajny, ale posiadać mało elastyczny interfejs.

Dokumentacja wykorzystania wzorcowych rozwiązań w systemie powinna przynajmniej


wyszczególniać każde z tych rozwiązań. Użycie danego wzorca projektowego zwykle nie jest
bezpośrednio widoczne w kodzie programu, bowiem nazwy klas uczestniczących w tym wzor-
cu różnią się od nazw wykorzystywanych w jego standardowej specyfikacji. Wiele wzorców
okazuje się pożytecznych głównie dzięki odseparowaniu od siebie pewnych klas (na przykład
klienta Mostu od jego implementacji), zatem klasy te powinny konsekwentnie pozostawać
rozseparowane w trakcie przyszłych zmian wprowadzanych do systemu. Podobnie jawne opi-
sanie, która klasa korzysta z których gotowych dokumentów, ułatwi późniejsze przystosowy-
wanie klas klienckich do nowszych wersji wspomnianych komponentów.
Podobnie pożyteczne okażą się powiązania wykorzystywanych gotowych rozwiązań
z odnośnymi fragmentami kodu źródłowego, zapisane jako uzupełnienie standardowej do-
kumentacji projektowania obiektów (patrz rozdział 9. „Projektowanie obiektów: specyfiko-
wanie interfejsów"). Znaczącym czynnikiem zwiększania kosztu przyszłych zmian w sys-
temie jest utrata kontekstu projektowego: programiści szybko zapominają o przyczynach,
dla których użyli takiej czy innej skomplikowanej struktury danych bądź dla których zastoso-
wali takie, a nie inne obejście (jakiegoś) problemu. Wzrasta wówczas ryzyko wprowadzenia
nowych błędów do systemu. W tym kontekście (nomen omen) krytycznego znaczenia nabiera
dokumentowanie wszelkich kompromisów, przykładów, rozwiązań alternatywnych i innych
istotnych informacji tego typu. Problemem systematycznego zbierania i dokumentowania
takich informacji zajmiemy się dokładniej w rozdziale 12. „Zarządzanie racjonalizacją".

8.5.2. Przydzielanie odpowiedzialności


Trudno oczekiwać od poszczególnych programistów, że spontanicznie rzucą się do wykorzy-
stywania gotowych wzorców i komponentów, jeśli nie mają w tym temacie odpowiedniego
doświadczenia. Można ich jednak do tego zachęcić, organizując łatwy dostęp do niezbędnej
wiedzy, a konkretnie — do porad ze strony doświadczonych programistów. Zmniejszy to zde-
cydowanie frustrację związaną z opanowywaniem nowej, trudnej wiedzy. Także eksponowanie
w czasie przeglądów systemu wykorzystywania (albo niewykorzystywania) gotowych kom-
ponentów zwiększyć może atrakcyjność tej techniki wśród programistów. W związku z tym,
menedżer projektu może wyznaczyć odpowiednim programistom role podobne do poniższych:

• Ekspert komponentu. To programista posiadający obszerną wiedzę na temat okre-


ślonego komponentu, być może uzyskaną w drodze szkolenia u producenta tegoż
komponentu.
• Ekspert wzorca projektowego. To rola analogiczna do roli eksperta komponentu,
programista pełniący tę rolę posiada doświadczenie w zakresie stosowania jednego
lub kilku wzorców. Doświadczenie to zdobywa jednak zwykle we własnym zakresie.
390 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

• Dokumentalista techniczny. Dokumentalista musi być świadom wykorzystywania


poszczególnych wzorców i komponentów oraz powinien dokumentować zależności
między komponentami, wzorcami projektowymi i systemem, zgodnie z dyskusją
w poprzedniej sekcji. Wymaga to znajomości zwyczajowo stosowanych w firmie
rozwiązań i dotyczącej tych rozwiązań terminologii.
• Menedżer konfiguracji. W uzupełnieniu do śledzenia konfiguracji i wersji poszcze-
gólnych podsystemów, menedżer konfiguracji musi być także świadom wersji używa-
nych komponentów. Gdy tylko pojawią się nowe wersje komponentów, ich zasto-
sowanie (oczywiście, udokumentowane) musi być poprzedzone stosownymi testami.

Techniczne środki umożliwiające korzystanie z gotowych rozwiązań (dziedziczenie,


delegowanie, wzorce projektowe, frameworki aplikacyjne) dostępne są dla programistów od
prawie dwudziestu lat. Sukces w ich wykorzystywaniu uwarunkowany jest dziś jednak nie tyle
zabiegami technologicznymi, ile menedżerskimi. Tylko firmy z wypracowaną kulturą korzy-
stania z gotowych wzorców projektowych i komponentów, posiadające narzędzia umożliwiające
skuteczną selekcję dostępnej ich oferty, mogą odnosić rzeczywiste korzyści z ich stosowania.

8.6. Analiza przypadku — system ARENA


Pokażemy teraz, jak w systemie ARENA wykorzystać można trzy wzorce projektowe. Zgodnie
z wymaganiami, system obsługiwać ma wiele różnych typów gier, w tej sekcji skupimy się więc
na klasach związanych z grami (Game), meczami (Match) i na obiektach brzegowych. Realizując
postulat niezależności systemu od konkretnej gry, wykorzystamy trzy następujące wzorce
projektowe:

• Fabrykę abstrakcyjną (patrz sekcja 8.6.1) w celu oddzielenia obiektów Tournament


i League od obiektów specyficznych dla konkretnych gier. Rolę fabryki pełnić będzie
klasa Game, stanowiąca abstrakcję dowolnej gry. Dzięki temu dla konkretnej gry
automatycznie tworzone będą obiekty właściwych typów, bez żadnego nadzoru ze
strony systemu.
• Polecenie (patrz sekcja 8.6.2) — ten wzorzec posłuży nam do odizolowania obiektów
związanych z rozgrywaniem i odtwarzaniem meczów (abstrakcyjna klasa Move repre-
zentująca ruchy graczy) od obiektów specyficznych dla konkretnych gier.
• Obserwator (patrz sekcja 8.6.3), dzięki któremu nadamy standardową formę inte-
rakcjom występującym między obiektami encji Match i Move a obiektami widoków
MatchVi ew, dla wszystkich gier, za pomocą paradygmatu „subskrybent-publikator".

Ograniczymy się przy tym do gier polegających na wymianie ruchów (Move) między
graczami (PI ayer), pomijając na razie problem gier obejmujących równoległe akcje ze strony
wielu graczy.

8.6.1. Zastosowanie wzorca projektowego Fabryka abstrakcyjna


Osiągnięcie niezależności systemu ARENA od konkretnej gry nie jest zadaniem tak prostym, jak
mogłoby się zrazu wydawać. W modelu analitycznym (patrz rysunek 8.16) zdefiniowaliśmy
10.6. Analiza przypadku — system ARENA 391

Rysunek 8.16. Obiekty modelu analitycznego systemu ARENA związane z jego niezależnością od kon-
kretnej gry

abstrakcyjny interfejs Game, oddzielający obiekty Tournament i League od obiektów reprezen-


tujących konkretne gry. Problem jednak w tym, że wsparcie systemu dla konkretnej gry
wymaga ścisłej współpracy wielu obiektów reprezentujących reguły tej gry (Game), stany trwa-
jących meczów (Match) oraz wyników (Result) gromadzonych w statystykach ( S t a t i s t i c s )
turniejów i lig. Musimy zatem zbudować w systemie ARENA środowisko oddzielające obiekty
Tournament i League od konkretnych gier (specjalizacji klasy Game), przy jednoczesnym za-
chowaniu standardowych interakcji między specjalizacjami klas Game, Match, Move, Result
i Statistics.
Zaczniemy od wzorca projektowego Fabryka abstrakcyjna, realizując cel projektowy
niezależności systemu od konkretnej gry (patrz rysunek 8.17). Abstrakcyjny interfejs Game
pełni rolę wspomnianej fabryki, udostępniając metody createMatch() i c r e a t e S t a t i s t i c s Q ,
za pomocą których tworzone są specjalizacje obiektów8 Match i Stati s t i cs dla konkretnej gry
— w kółko i krzyżyk (TicTacToe) albo szachów (Chess): dla pierwszej z wymienionych są to
obiekty TTTMatch i TTTStats, dla drugiej — obiekty ChessMatch i ChessStats. Konkretne spe-
cjalizacje obiektu Match (TTTMatch i ChessMatch) odpowiedzialne są za śledzenie stanu trwa-
jących meczów i wymuszanie reguł określonych przez specjalizacje obiektu Game (Ti cTacToe
i Chess). Każdy z tych ostatnich obiektów posiada stowarzyszony obiekt będący specjalizacją
obiektu S t a t i s t i cs, odpowiedzialny za gromadzenie statystyki wartości średnich (między
innymi średniej długości meczu w danej grze, średniej liczby ruchów w meczu danej gry, licz-
by przegranych i wygranych w danej grze dla każdego z graczy i specyficznych statystyk zwią-
zanych z samą grą). Specjalizacje obiektu Stati s t i cs przypisane są także do każdego turnieju
(Tournament) i każdej ligi (Leaciuel w celu gromadzenia statystyk na poziomie poszczegól-
nych turniejów i lig. Ponieważ ol i v Tournament i League odwołują się do całej opisanej
reszty za pośrednictwem abstrrl n ; c h interfejsów Game, Match i S t a t i s t i c s , funkcjonują
identycznie dla wszystkich gier zgounych ze strukturą systemu ARENA.

8
Dla uproszczenia piszemy o „specjalizacjach obiektów", choć prawidłowo powinno się pisać o „specja-
lizacjach klas": dla uproszczenia, pod pojęciem „specjalizacji obiektu T" rozumieć będziemy „obiekt bę-
dący instancją klasy stanowiącej specjalizację klasy T" — przyp. tłum.
392 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

Rysunek 8.17. Zastosowanie wzorca projektowego Fabryka abstrakcyjna w systemie ARENA

8.6.2. Zastosowanie wzorca projektowego Polecenie


Mimo iż kibice ( S p e c t a t o r ) mogą oglądać mecze (Match) „na żywo", przewidujemy także
konieczność zapewnienia odtwarzania meczów już zakończonych. Do tego potrzebne jest
rejestrowanie sekwencji ruchów w każdym meczu, które to sekwencje mogą być odtwa-
rzane na żądanie.
Jak już pisaliśmy w sekcji 8.4.5, zrealizujemy to zadanie za pomocą wzorca projektowego
Polecenie, w ramach którego każdy ruch reprezentowany będzie jako specjalizacja obiektu
Command-. Odpowiadający m u obiekt Move dostarcza w tym celu odpowiedni interfejs dla
obiektów Tournament i League, umożliwiając im manipulowanie kolejnymi ruchami graczy
w sposób niezależny od konkretnej gry (Game) i konkretnego meczu (Match). Poszczególne
ruchy rejestrowane są — w postaci obiektów stanowiących specjalizacje obiektów Move —
w kolejce związanej z odpowiednim meczem (Match), tak jak na rysunku 8.18.

Rysunek 8.18. Zastosowanie wzorca projektowego Polecenie do rejestrowania i odtwarzania ruchów


graczy w poszczególnych meczach
10.6. Analiza przypadku — system ARENA 393

Z odtwarzaniem archiwalnych meczów wiąże się kolejny problem, wynikający z ko-


nieczności obsługi wielu kibiców chcących oglądać ten sam mecz równocześnie (i w różnych
stadiach zaawansowania). Niezależność tę realizują obiekty Repl ayedMatch. Każdy z nich
posiada własną planszę (GameBoard) przechowującą aktualny stan odtwarzanego meczu
(Match), z którego kolejki czerpie kolejno specjalizacje obiektów Move reprezentujące kolejne
ruchy. Kolejne obiekty Repl ayedMatch mogą w danym czasie odczytywać tę kolejkę w różnych
miejscach, co uniezależnia od siebie poszczególnych kibiców.

8.6,3. Zastosowanie wzorca projektowego Obserwator


Gry rozgrywane na forum systemu ARENA mogą angażować wielu graczy. Każdy z nich uczest-
niczy w meczu za pośrednictwem przeglądarki W W W w swoim komputerze, a to oznacza,
że z danym meczem związanych jest jednocześnie wiele widoków, które muszą być utrzymy-
wane w spójny sposób. Zadanie to zrealizujemy za pomocą rozproszonej wersji wzorca pro-
jektowego Obserwator (patrz rysunek 8.19). Rolę obserwatorów (Observer) pełnią tu od-
powiednie obiekty brzegowe każdego klienta, zaś obserwowanym tematem (Subject) jest
plansza gry (GameBoard). Powiązania między obiektami Observer i Subject realizowane są za
pomocą zdalnych referencji oferowanych przez Java RMI. Oprócz utrzymywania spójności
każdego widoku z obserwowanym meczem, rozwiązanie to umożliwia zastosowanie tego sa-
mego schematu dla wszystkich obiektów Repl ayedMatch.

Rysunek 8.19. Zastosowanie wzorca projektowego Obserwator do utrzymywania spójności widoków


w systemie ARENA

8.6.4. Wnioski
Pokazaliśmy praktyczne zastosowanie trzech wybranych wzorców projektowych w realizacji
celu, jakim jest niezależność systemu ARENA od konkretnych gier. Nawet ten prosty przykład
dostarczył dowodów na to, że:

• Zastosowanie różnych wzorców projektowych może się nakładać w tym samym


systemie, przykładowo klasa Match uczestniczy w dwóch wzorcach: we wzorcu
Fabryka abstrakcyjna pełni rolę abstrakcyjnego produktu (AbstractProduct-),
zaś we wzorcu Polecenie — rolę inicjatora poleceń (Invoker).
394 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

• Wybór odpowiedniego wzorca projektowego nie jest łatwym zadaniem. Dostępnych


wzorców projektowych jest dużo, są zebrane w różnych katalogach i ich trafna ocena
pod kątem przydatności w danym zastosowaniu wymaga sporego doświadczenia. To
ważki argument na rzecz dokumentowania wzorców projektowych także w kontekście
przykładów ich zastosowań, na użytek programistów mniej doświadczonych.
• Wzorce projektowe wymagają przystosowywania. Każdy wzorzec projektowy jest
jedynie „surowym" szablonem, który musi zostać zaadaptowany do specyfiki kon-
kretnego problemu. W procesie tej adaptacji należy bardzo uważnie przeanalizować
każde użycie dziedziczenia i delegowania, by nie zniwelować korzyści wnoszonych
przez użyty wzorzec projektowy.

8.7. Literatura uzupełniająca


Dziedziczenie klas stawia przed programista wiele wyzwań. Mimo iż dostarcza potężnego me-
chanizmu umożliwiającego budowanie klas i komponentów wielokrotnego użytku, to jedno-
cześnie pozwala na tworzenie skomplikowanych struktur sterowania, trudnych w zrozumie-
niu i testowaniu, prowadzących do kreowania zagmatwanych systemów. Od samego początku,
gdy tylko pojawiło się na gruncie języków programowania, teoretycy i badacze podejmowali
próby odróżnienia „złego" użycia dziedziczenia od „dobrego" i opracowania konkretnych
zasad projektowych w tym temacie.
Marvin Minsky jest pomysłodawcą wykorzystania dziedziczenia na gruncie firameworku
Frames, służącego do reprezentowania wiedzy w systemach sztucznej inteligencji [Minsky,
1975], Koncepcja dziedziczenia została nieco później udoskonalona i pojawiła się na gruncie
pierwszych obiektowych języków programowania, takich jak Smalltalk opisany w pracy A. Gold-
berg i A. Kay [Goldberg i Kay, 1976]. Rumbaugh i jego koledzy wprowadzili po raz pierwszy
dziedziczenie do modelowania, w postaci języka o nazwie OMT [Rumbaugh i in., 1991], który
wywarł duży wpływ na postać języka UML.
Zasada zastępowania Liskov [Liskov, 1988], określająca formalne zasady rozróżniania
między dziedziczeniem specyfikacyjnym a implementacyjnym i definicję podtypu, jakkolwiek
prosta i zrozumiała, trudna jest w zastosowaniu jako zasada projektowania.
W swojej książce [Meyer, 1997], której pierwsze wydanie ukazało się w roku 1989, B. Meyer
formułuje zasadę, że klasy abstrakcyjne powinny być otwarte na rozszerzanie i jednocześnie
zamknięte pod względem możliwości ich modyfikowania. Zasadę ta rozwinięta jest książce
J. Martina i J. J. Odella [Martin i Odell, 1992],
Prawdziwym przełomem było ukazanie się książki E. Gammy, R. Heima, R. Johnsona
i J. Vlissadesa [Gamma i in., 1994] definiującej nowe podejście do wielokrotnego wykorzy-
stywania gotowych rozwiązań, oferującej szablonowe rozwiązania dla problemów najczęściej
pojawiających się w każdym niemal projekcie. Przez kombinowanie, krzyżowanie i nakładanie
poszczególnych wzorców programiści zyskują potężne narzędzie do rozwiązywania wielu
problemów związanych z rozszerzalnością i wielokrotnym wykorzystywaniem tego samego
kodu. Koncepcja reprezentowania wiedzy w postaci wzorców stała się tak popularna, że inni
autorzy postanowili rozszerzyć ją na inne obszaiy zastosowań, takie jak architektura oprogra-
mowania, opisana przez F. Buschmanna, R. Meuniera, H. Rohnerta, P. Sommerlada i M. Stała
[Buschmann i in., 1996], i analiza wymagań omówiona przez M. Fowlera [Fowler, 1997].
8.8. Ćwiczenia 395

8.8. Ćwiczenia
8.1. Dla każdego z poniższych obiektów modelu systemu ARENA wskaż przynależność
do konkretnej dziedziny — aplikacyjnej albo realizacyjnej:
• League,

• LeagueStore,

• LeagueXMLStorelmpl ementor,

• Match,

• MatchVi ew,

• Move,

• ChessMove.

8.2. Które z poniższych przykładów dziedziczenia odzwierciedlają dziedziczenie spe-


cyfikacyjne, a które dziedziczenie implementacyjne?
• Klasa Rectangl e (prostokąt) dziedzicząca po klasie Polygon (wielokąt).
• Klasa Set (zbiór) dziedzicząca po klasie Bi naryTree (drzewo binarne).
• Klasa Set dziedzicząca po klasie Bag (nieuporządkowana kolekcja).
• Klasa PI ayer (gracz w systemie ARENA) dziedzicząca po klasie User (użytkownik
systemu ARENA).
• Klasa Window (okno) dziedzicząca po klasie Polygon.
8.3. Przypuśćmy, że chcemy zintegrować z systemem ARENA popularny brydż, zaimple-
mentowany w języku Java. Który wzorzec projektowy będzie najodpowiedniejszy do
tego celu? Narysuj diagram klas UML odnoszący się do obiektów systemu ARENA,
zawierający niektóre klasy specyficzne dla brydża.
8.4. Rozpatrzmy system organizujący pracę programistów, umożliwiający menedżerowi
modelowanie tej pracy w kategoriach aktywności i produktów. Dla każdego z pro-
gramistów menedżer ustala listę aktywności oraz terminy dostarczania poszczególnych
produktów. W systemie przewidziano obsługę różnych typów produktów: sformato-
wanego testu, grafiki i lokałizatorów URL. Menedżer edytujący przepływ pracy może
dynamicznie określać (zmieniać) typ istniejącego lub nowo dodawanego produktu.
Przyjmując za jeden z celów projektowych oczywiste założenie, iż system opisywany
musi być rozszerzalny o nowe typy produktów, jaki wzorzec projektowy wybrałbyś
do ich reprezentowania?
8.5. Wyobraźmy sobie aplikację bazodanową składającą się ze strony klienckiej i dwóch
redundantnych serwerów. Oba serwery są identyczne, drugi pełni rolę zapasowego
na wypadek awarii pierwszego. Dostęp klienta do strony serwerowej odbywa się
za pośrednictwem komponentu zwanego „bramą", klient nie wie zatem, do którego
z serwerów jest aktualnie podłączony. Osobny obiekt, zwany „Cerberem", monitoruje
ciąg żądań napływających do serwera głównego oraz produkowanych przez niego
odpowiedzi i zależnie od wyników tego monitorowania może wysłać do wspomnia-
nej „bramy" żądanie przełączenia serwera na zapasowy. Jak nazwałbyś ten wzorzec
projektowy? Uzasadnij swoją odpowiedź, rysując odpowiedni diagram klas UML.
396 Rozdział 8. • Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzorcowych

8.6. W sekcji 8.4.1 opisaliśmy wykorzystywanie wzorca projektowego Most do zaim-


plementowania podsystemu LeagueStore, w celu ułatwienia testowania jego in-
tegracji z innymi podsystemami. Byłoby wspaniale, gdyby ten zabieg można było
zastosować do pozostałych podsystemów (w celu łatwiejszego ich testowania), lecz
— niestety — nie zawsze jest to możliwe. Podaj przykład podsystemu, w którym nie
da się zastosować wzorca projektowego Most.
8.7. Wskaż listę wzorców projektowych, których zastosowanie rozważyłbyś dla każdego
z poniższych celów projektowych:
• Należy wykorzystać komponent realizujący logikę biznesową w obecnie eksplo-
atowanej aplikacji bankowej.
« Dla tworzonego programu szachowego należy uwzględnić możliwość przyszłej
wymiany algorytmu planowania kolejnego ruchu na lepszy wariant.
• Należy wyposażyć program szachowy w komponent monitorujący styl gry oraz
czas reakcji przeciwnika i na tej podstawie przełączający dynamicznie algorytmy
planowania kolejnego ruchu.
• Dla symulacji myszy próbującej wydostać się z labiryntu trzeba zapewnić kompo-
nent dokonujący ewałuacji różnych ścieżek, niezależny od typów ruchów przewi-
dzianych dla symulowanej myszy.
8.8. Jaki wzorzec projektowy wybrałbyś dla aplikacji wybierającej dynamicznie algorytm
szyfrowania, w zależności od bieżąco wymaganego stopnia zabezpieczeń i dostępnej
aktualnie mocy obliczeniowej? Uzasadnij swój wybór, rysując diagram UML przed-
stawiający klasy wspomnianego wzorca.

Bibliografia
[BEA] BEA WebLogic Platform, http://www.bea.com/products/weblogic/platform.
[Birrer, 1993] E. T. Birrer „Frameworks in the financial engineering domain: An experience
report", ECOOP'93 Proceedings, Lecture Notes in Computer Science,
n r 707, 1993.
[Bruegge i in., 1993] B. Bruegge, T. Gottschałk, B. Luo A framework for dynamic program analyzers,
OOPSLA' 93, (Object-Oriented Programming Systems, Languages,
and Applications), Washington DC, str. 65 - 82, wrzesień 1993.
[Buschmann i in., 1996] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, M. Stal Pattern-Oriented
Software Architecture: A System of Patterns, Wiley, Chichester, U.K., 1996.
[Campbell i Islam, 1993] R. H. Campbell, N. Islam A technique for documenting the framework of
an object-oriented system, „Computing Systems", nr 6, str. 363 - 389, 1993.
[Fayad i Hamu, 1997] M. E. Fayad, D. S. Hamu Object-oriented enterprise frameworks: Make vs. buy
decisions and guidelines for selection, „The Communications of ACM", 1997.
[Fowler, 1997] M. Fowler Analysis Patterns: Reusable Object Models, Addison-Wesley,
Reading, MA, 1997.
[Gamma i in., 1994] E. Gamma, R. Helm, R. Johnson, J. Vlissides Design Patterns: Elements of
Reusable Object-Oriented Software, Addison-Wesley, Reading, MA, 1994.
Wydanie polskie Wzorce projektowe. Elementy oprogramowania obiektowego
wielokrotnego użytku, Helion 2010.
Bibliografia 397

[Goldberg i Kay, 1976] A. Goldberg, A. Kay Smalltalk-72 Instruction Manual, Xerox Palo Alto, CA, 1976.
[IBM] IBM WebSphere Software Platform for E-Business,
http://www. ibm.com/websphere/.
[JavaEE, 2009] Java Platform, Enterprise Edition, Javasoft 2009, http://java.sun.com/.
[JFC, 2009] Java Foundation Classes, JDK Documentation. Javasoft 2009.
R. Johnson, B. Foote Designing reusable classes, „Journal of Object-Oriented
[Johnson i Foote, 1988] Programming", t. 1, nr 5, str. 22 - 35, 1988.
B. Liskov Data abstraction and hierarchy, „SIGPLAN Notices", t. 23,
[Liskov, 1988] nr 3, maj 1988.
J. Martin, J. J. Odell Object-Oriented Analysis and Design, Prentice Hall,
[Martin i Odell, 1992] Englewood Cliffs, NJ, 1992.
B. Meyer Object-Oriented Software Construction, wyd. drugie, Prentice
[Meyer, 1997] Hall, Upper Saddle River, NJ, 1997.
M. Minsky „A framework for representing knowledge," w P. Winston
[Minsky, 1975] (red.) The Psychology of Computer Vision, McGraw-Hill, 1975.
Object Management Group, Common Object Request Broker Architecture
[OMG, 2008] (CORBA) Specification: Version 3.1, http://www.omg.org 2008.
J. Rumbaugh, M. Blaha, W. Premerlani, F. Eddy, W. Lorensen Object-
[Rumbaugh i in., 1991] Oriented Modeling and Design, Prentice Hall, Englewood Cliffs, NJ, 1991.
D.C. Schmidt „Applying design patterns and frameworks to develop
[Schmidt, 1997] object oriented communication software" w Peter Salus (red.) Handbook
of Programming Languages, t. 1, MacMillan Computer, 1997.
[Weinand i in., 1988] A. Weinand, E. Gamma, R. Marty „ET++ — An object-oriented application
framework in C++" w Object-Oriented Programming Systems, Languages,
and Applications Conference Proceedings, San Diego, CA, wrzesień 1988.
[Wilson i Ostrem, 1999] G. Wilson, J. Ostrem WebObjects Developer's Guide, Apple, Cupertino, CA, 1998.
9.1. Wstęp: kolej miejska i tramwaje 400

9.2. O specyfikowaniu interfejsów ogólnie 401

9.3. Koncepcje specyfikowania interfejsów 403


9.3.1. Implementator, ekstender i użytkownik klasy 403
9.3.2. Typy, sygnatury i widzialność 403
9.3.3. Kontrakty: niezmienniki, warunki wstępne i warunki końcowe 406
9.3.4. Język OCL (Object Constraint Language) 407
9.3.5. Kolekcje OCL: zbiory, wielozbiory i ciągi 411
9.3.6. Kwantyfikatory OCL: forAllO i exists() 415
9.4. Aktywności specyfikowania interfejsów 416
9.4.1. Identyfikowanie brakujących atrybutów i operacji 417
9.4.2. Specyfikowanie typów, sygnatur i widzialności 418
9.4.3. Specyfikowanie warunków wstępnych i warunków końcowych 419
9.4.4. Specyfikowanie niezmienników 421
9.4.5. Dziedziczenie kontraktów 424

9.5. Zarządzanie projektowaniem obiektów 425


9.5.1. Dokumentowanie projektowania obiektów 425
9.5.2. Przydzielanie odpowiedzialności 431
9.5.3. Wykorzystywanie kontraktów w analizie wymagań 432

9.6. Analiza przypadku — system ARENA 433


9.6.1. Identyfikowanie brakujących operacji
w klasach TournamentStyle i Round 434
9.6.2. Specyfikowanie kontraktów dla klas TournamentStyle i Round 435
9.6.3. Specyfikowanie kontraktów dla klas KnockOutStyle
i KnockOutRound 438
9.6.4. Wnioski 439

9.7. Literatura uzupełniająca 440

9.8. Ćwiczenia 440

Bibliografia 442
Projektowanie obiektów:
specyfikowanie interfejsów

Jeśli masz procedurę z dziesięcioma parametrami,


prawdopodobnie zapomniałeś o niektórych.
— Alan Perlis, Epigrams in Programming

W czasie projektowania obiektów identyfikujemy i doskonalimy obiekty realizacyjne pod-


systemów zdefiniowanych na etapie projektu systemu. Nasze rozumienie poszczególnych obiek-
tów staje się coraz lepsze: definiujemy sygnaturę typów i widzialność każdej operacji oraz opi-
sujemy warunld, których spełnienie konieczne jest do wywołania danej operacji, a także te,
w których operacja ta powinna generować wyjątek. Podczas gdy celem projektowania syste-
mu był „gruboziarnisty" podział systemu na podsystemy przydzielane poszczególnych pro-
gramistom lub zespołom, projektowanie obiektów sprowadza się do „drobnoziarnistego" de-
finiowania granic między poszczególnymi obiektami. Na tym etapie projektów duża liczba
programistów równolegle opracowuje i doskonali obiekty oraz ich interfejsy. Coraz bardziej
odczuwalna staje się presja na terminowe dostarczanie produktów, a w konsekwencji zwiększa
się ryzyko wprowadzenia błędów do projektu. Ryzyko to można wydatnie zmniejszyć, zapew-
niając efektywne i precyzyjne komunikowanie się programistów w kwestii coraz większej liczby
niskopoziomowych szczegółów systemu. Temu celowi służy specyfikowanie interfejsów, obej-
mujące między innymi następujące aktywności:

• identyfikowanie przeoczonych atrybutów i operacji,


• specyfikowanie sygnatur typów i widzialności,
• specyfikowanie niezmienników,
• specyfikowanie warunków wstępnych i końcowych.

W tym rozdziale dokonamy przeglądu koncepcji związanych ze specyfikowaniem in-


terfejsów. Przedstawimy podstawowe elementy języka OCL (Object Constraint Language)
służącego do definiowania niezmienników, warunków wstępnych i warunków końcowych,
przedyskutujemy heurystyki i wskazania stylistyczne pomagające w czytelnym formuło-
waniu ograniczeń. Na koniec zajmiemy się problemami związanymi z zarządzaniem aktyw-
nościami chodzącymi w skład specyfikowania interfejsów oraz dokumentowaniem tych
aktywności.
400 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

9.1. Wstęp: kolej miejska i tramwaje


Stuttgart: od tramwajów do kolei miejskiej
Aż do roku 1976 masowy transport publiczny Stuttgartu oparty był na tramwajach. Z rozległej sieci
tramwajowej korzystało mnóstwo pasażerów, sieć ta w pewnym momencie osiągnęła jednak nasy-
cenie: ograniczenia wynikające zarówno z niewielkiej pojemności wagonów, jak i rosnącego ruchu
samochodowego zmusiły do poszukiwania lepszego rozwiązania — rozsądną alternatywą okazała
się „lekka kolej" miejska. Większy rozstaw szyn takiej kolei, w porównaniu z torowiskiem tram-
wajowym, oznaczał większą pojemność taboru, a większa prędkość handlowa 1 oznaczała zwięk-
szoną przepustowość przewozów; co więcej, ponieważ kolej miejska jest z założenia niezależna od
ruchu samochodowego, można dla niej układać rozkłady jazdy realizowalne nawet w godzinach
szczytu. Zapoczątkowana w 1976 roku konwersja tramwajów na lekką kolej okazała się przedsię-
wzięciem gigantycznym w kontekście rozmiarów sieci i długości torowisk, było więc oczywiste, iż
musi odbywać się ewolucyjnie, stopniowo: dla niektórych linii oznaczało to tymczasowe kursowa-
nie obu środków lokomocji — tramwajów i kolei. Środków — oczywiście — niekompatybilnych, co
wiązało się z problemami dotyczącymi przede wszystkim:

• rozstawu szyn. Kolej miejska poruszała się po torowisku o standardowym rozstawie 1450 mm,
podczas gdy tramwaje były taborem „metrowym", czyli korzystającym z rozstawu 1000 m m .
Uniwersalne torowisko umożliwiające ruch obu typów taboru musiało więc składać się z trzech,
nie dwóch szyn, co — oprócz zwiększonego zużycia stali — przekładało się natychmiast na
bardziej skomplikowane zwrotnice.

wysokości peronów. Tramwaje przystosowane są do wsiadania z poziomu ulicy, podczas gdy


wagony kolejowe dostosowane są do wysokich peronów. Na niektórych przystankach konieczne
było więc zbudowanie peronów dwuczęściowych; ponieważ każda z części musiała być tak
samo długa jak długość odpowiadającego jej taboru, oznaczało to mniej więcej dwukrotne
wydłużenie peronu.

• systemu sygnalizacji. T r a m w a j e korzystają z sygnalizacji ulicznej, tej samej co samochody,


a to oznacza mechaniczne, naprzemienne kierowanie ruchem na skrzyżowaniach, niezależnie
od natężenia tego ruchu. Sygnalizacja kolei miejskiej jest natomiast oparta na ogólnych stdardardach
ruchu kolejowego, między innymi na centralnym sterowaniu z nastawni, zależnie od wyników
bieżącego monitorowania ruchu. Ponieważ w okresie przejściowym kolej miejska z konieczności
musiałaby się wpisywać w ruch uliczny, jej sygnalizacja musiałaby być zsynchronizowana
z sygnalizacją uliczną.

1
Prędkość handlowa środka transportu jest średnią jego prędkością na danej trasie, czyli ilorazem długości
tej trasy przez czas, w jakim została przebyta. Zależy nie tylko od możliwości samego pojazdu, lecz także
od bieżących warunków, w jakich się porusza, między innymi postojów na przystankach pośrednich
i zatłoczenia („zakorkowania") dróg, jest zatem miarodajnym czynnikiem oceny środka transportu
z perspektywy jego pasażera — przyp. tłum, na podstawie pl.wikipedia.org.
9.2. O specyfikowaniu interfejsów ogólnie 401

W kontekście rozciągnięcia w czasie opisanej modernizacji system dualnego taboru znakomicie zdał
egzamin: udało się sprostać rosnącym potrzebom transportu publicznego, również dzięki wyelimi-
nowaniu konieczności wyłączania z ruchu poszczególnych linii. Cały projekt zakończył się szczę-
śliwie w grudniu 2007 roku, kiedy to z ulic Stuttgartu zniknął ostatni tramwaj.

Powyższy przykład znakomicie ilustruje koncepcję interfejsu: pojazd szynowy, podobnie


jak obiekt programu, dostarcza klientom pewne usługi pod warunkiem spełnienia przez śro-
dowisko określonych wymogów. Z perspektywy torowiska interfejsem tramwaju są jego koła
— gdy zmienia się rozstaw kół pojazdu, odpowiednio musi zmienić się rozstaw szyn. Z per-
spektywy podróżnego (pasażera) interfejsem pojazdu są jego drzwi: jeśli schody w drzwiach
usytuowane są wyżej, podróżni potrzebują wyższego peronu. Wreszcie, z perspektywy systemu
sygnalizacji interfejsem pojazdu jest jego kierowca (motorniczy, maszynista): wprowadzenie
nowego systemu sygnalizacji wymaga odpowiedniego przeszkolenia kierujących pojazdami.
Każdy z wymienionych elementów interfejsu (rozstaw kół, wysokość schodów, inter-
pretacja sygnałów przez kierującego) musi pozostawać w zgodzie z odpowiednim elementem
środowiska — rozstawem szyn, wysokością peronu i systemem sygnalizacji. Identyczna zasada
stosuje się do tworzenia oprogramowania: każdy obiekt współdziała z innymi obiektami za
pośrednictwem swego interfejsu obejmującego zarówno zbiór operacji o określonych para-
metrach i (ewentualnym) określonym wyniku, jak i zbiór założeń na temat znaczenia poszcze-
gólnych operacji. Gdy zmieni się postać wspomnianego interfejsu — to znaczy gdy zmieni się
definicja którejś operacji bądź przestaną być spełnione związane z nią założenia — obiekt
nie będzie w stanie świadczyć swych usług na dotychczasowych warunkach.
W tym rozdziale skupimy się na aktywnościach związanych ze specyfikowaniem inter-
fejsów. W sekcji 9.2 przedstawimy ogólnie problematykę specyfikowania interfejsów, w sekcji 9.3
zajmiemy się natomiast głównymi koncepcjami interfejsu — sygnaturami typów, ogranicze-
niami i kontraktami, po czym zaprezentujemy podstawowe elementy języka OCL służącego
do specyfikowania ograniczeń. Zastosowanie opisanych koncepcji omówimy w sekcji 9.4 na
przykładzie systemu ARENA. Sekcję 9.5 poświęcimy zagadnieniom menedżerskim związanym
ze specyfikowaniem interfejsów, między innymi przydzielaniu odpowiedzialności i doku-
mentowaniu interfejsów, całość zaś zakończy sekcja 9.6 dedykowana systemowi ARENA.

9.2. O specyfikowaniu interfejsów ogólnie


Rozpoczynamy etap specyfikowania interfejsów, mamy już za sobą wiele ważnych decyzji i dys-
ponujemy bogatym zbiorem modeli, mianowicie:

• analitycznym modelem obiektowym opisującym obiekty encji, obiekty brzegowe


i obiekty sterujące widoczne dla użytkownika, wraz z atrybutami i operacjami każdego
z tych obiektów,
• wynikiem dekompozycji systemu w postaci jego podziału na spoiste podsystemy,
z których każdy realizowany jest przez osobny zespół programistów; dla każdego
z tych podsystemów istnieje wysokopoziomowy opis usług świadczonych na rzecz
innych podsystemów,
402 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

• odwzorowaniem sprzętowo-programowym identyfikującym budowaną maszynę wir-


tualną, dla której tworzone są obiekty realizacyjne; elementami tego odwzorowania
są klasy i interfejsy API definiowane w ramach istniejących komponentów,
• zbiorem granicznych przypadków użycia opisujących, z perspektywy użytkownika,
czynności administrowania systemem oraz sytuacje wyjądcowe, którym system ten
musi sprostać,
• wzorcami projektowymi wybranymi na etapie wykorzystywania gotowych roz-
wiązań, dedykowanymi częściowemu rozwiązywaniu poszczególnych problemów
projektowych.

Każdy z tych modeli odzwierciedla jedynie fragmentaryczne spojrzenia na system; są one


elementami swoistej układanki, w której jednak brakuje jeszcze wiele elementów, a niektóre
z elementów wymagają przystosowania. Stajemy zatem przed zadaniem stworzenia modelu
integrującego w sposób spójny i precyzyjny informację reprezentowaną przez wspomnianą
układankę. Aby poszczególne elementy tej układanki, opracowywane przez różne zespoły pro-
gramistyczne, można było do siebie dopasować, muszą się one charakteryzować precyzyjnie
określonymi interfejsami, uzgodnionymi między programistami — tak by faza ich integrowa-
nia odbyła się bezproblemowo (no, może „niemal bezproblemowo"). Specyfikowanie interfejsów
może być postrzegane w postaci trzech następujących aktywności:

• identyfikowania brakujących atrybutów i operacji. Analizując dokładnie usługi każdego


podsystemu i każdy obiekt modelu analitycznego, wykrywamy brakujące atrybuty
i operacje niezbędne do świadczenia wspomnianych usług i odpowiednio doskona-
limy model projektu obiektów.
• definiowania widzialności i sygnatur. W stosunku do poszczególnych operacji decy-
dujemy, które z nich mają być dostępne dla innych obiektów i podsystemów, a które
używane są tylko wewnętrznie przez podsystem. Dla każdej operacji (w obu kate-
goriach) określany ponadto liczbę i typy parametrów oraz typ wyniku. W ten oto
sposób redukujemy powiązania między podsystemami i tworzymy proste interfejsy,
zrozumiałe dla pojedynczych programistów.
• specyfikowania kontraktów. Każdą z operacji poszczególnych obiektów opisujemy
w kategoriach narzuconych na nią ograniczeń, określając między innymi warunki,
które muszą być spełnione przed wywołaniem operacji, i warunki, których spełnienie
jest gwarantowane po zakończeniu tej operacji.

Multum obiektów, duża liczba programistów pracujących nad tymi obiektami, wysoka
dynamika zmian i mnóstwo podejmowanych równolegle decyzji — wszystko to sprawia, że faza
projektowania obiektów jest znacznie bardziej skomplikowana niż analiza wymagań czy pro-
jektowanie systemu. Złożoność ta pociąga za sobą niebagatelne wyzwania dla menedżera
projektu, bowiem wiele wspomnianych decyzji podejmowanych jest niezależnie i większość
z nich nie jest zwykle komunikowana ogółowi uczestników.
Projektowanie obiektów łączy się z zapewnieniem dostępności dużej ilości informacji dla
dużej liczby programistów, tak by podejmowane przez nich decyzje były spójne i zgodne z celami
projektowymi. Informację taką zapewnia Dokument Projektu Obiektów, w skrócie ODD (Object
Design Document) — „żywy" dokument zawierający szczegółową specyfikację każdej klasy.
9.4. Aktywności specyfikowania interfejsów 403

9.3. Koncepcje specyfikowania interfejsów


W tej sekcji opiszemy następujące koncepcje dotyczące specyfikowania interfejsów:

• Implementator, ekstender i użytkownik klasy (patrz sekcja 9.3.1),


• Typy, sygnatury i widzialność (patrz sekcja 9.3.2),
• Kontrakty: niezmienniki, warunki wstępne i warunki końcowe (patrz sekcja 9.3.3),
• Język OCL (Object Constraint Language) (patrz sekcja 9.3.4),
• Kolekcje OCL: zbiory, wielozbiory i ciągi (patrz sekcja 9.3.5),
• Kwantyfikatory OCL: forAl 1 () i e x i s t s ( ) (patrz sekcja 9.3.6).

9.3.1. Implementator, ekstender i użytkownik klasy


Dotychczas nie czyniliśmy żadnego rozróżnienia między programistami, gdy jednak przychodzi
do szczegółów projektowania i implementowania obiektów, musimy spojrzeć na te czynności
z kilku punktów widzenia. Podczas gdy dla wszystkich programistów specyfikacja interfejsu
danej klasy jest podstawą wzajemnej komunikacji, to w kontekście tej specyfikacji każdy
programista mający z nią do czynienia pełnić może jedną z trzech następujących ról (patrz
rysunek 9.1). Oto one.

• Implementator klasy odpowiedzialny jest za jej realizację: zaprojektowanie we-


wnętrznych struktur i zaimplementowanie kodu wszystkich operacji. Specyfikacja
interfejsu tej klasy jest dla implementatora przydziałem pracy.
• Użytkownik klasy wykorzystuje operacje udostępniane przez daną klasę na po-
trzeby realizacji innej klasy (klas), zwanej klasą kliencką. Specyfikacja interfejsu
przedmiotowej klasy wyznacza w tym przypadku jej granice pod kątem oferowa-
nych usług i założeń przyjmowanych względem klas klienckich.
• Ekstender klasy zajmuje się specjalizacjami przedmiotowej klasy. Podobnie jak
użytkownik klasy, ekstender wykorzystuje oferowane przez nią operacje; podob-
nie jak implementator, ekstender implementuje nowe klasy, skupiając się jednak
na specjalizowaniu określonego zbioru usług. Specyfikacja interfejsu jest więc dla
ekstendera zarówno obrazem zachowania przedmiotowej klasy, jak i wyznaczni-
kiem ograniczeń narzuconych na usługi oferowane przez klasy specjalizowane.

W kontekście abstrakcyjnej klasy Game systemu ARENA (patrz rysunek 9.2) programista
opracowujący operacje wspólne dla wszystkich subklas tej klasy jest jej implementatorem.
Użytkownikami klasy Game są między innymi implementatorzy klas League i Tournament
— obiekty tych klas korzystają z klasy Game w celu organizowania i rozpoczynania meczów
(Match). Z kolei programiści implementujący klasy Ti cTacToe i Chess, będące subklasami
(specjalizacjami) klasy Game, są z jej perspektywy ekstenderami.

9.3.2. Typy, sygnatury i widzialność


Identyfikując podczas analizy wymagań atrybuty i operacje, nie zajmujemy się ich typami
i parametrami, na to bowiem przychodzi czas teraz, w czasie projektowania obiektów. Dla
404 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

Rysunek 9.1. Role implementatora, użytkownika i ekstendera klasy

Rysunek 9.2. Zależności klas systemu ARENA od klasy Game w kontekście ról programistycznych
przedstawionych na rysunku 9.1

każdego atrybutu określamy jego typ, dla każdej operacji — liczbę i typy jej parametrów oraz
typ wyniku. Dla każdego atrybutu i każdej operacji ustalamy też zakres ich widzialności. Pod
pojęciem typu atrybutu rozumiemy zbiór (zakres) wartości, jakie ów atrybut może przyjmo-
wać, oraz operacji, jakie mają zastosowanie do tego atrybutu. Przykładowo wartościami atrybutu
maxNumPl ayers klasy Tournament (patrz rysunek 9.3), określającego maksymalną liczbę graczy
(PI ayer), mogą być liczby całkowite, co jest odpowiednikiem typu i nt 2 . Do wielkości tego typu
stosują się operacje mające sens dla liczb całkowitych: porównywanie, dodawanie, odejmowa-
nie, mnożenie przez liczbę całkowitą i tym podobne.
Identyczne znaczenie ma pojęcie typu w odniesieniu do każdego z parametrów operacji
i do zwracanego przez nią wyniku. Wektor (krotkę) danej operacji, jaki tworzą kolejne typy
jej parametrów i typ wyniku, nazywamy jej sygnaturą. Przykładowo operacja acceptPl ayer()
klasy Tournament wymaga jednego parametru typu PI ayer i nie zwraca żadnej wartości, jej
sygnatura ma zatem postać (Pl ayer): voi d;podobnie operacja getMaxNumPl ayers () tej samej
klasy ma sygnaturę (voi d ) : i nt.
Programiści w każdej z trzech ról — implementatora, użytkownika i ekstendera klasy —
korzystają z operacji i atrybutów tej klasy. Zależnie jednak od konkretnej, roli mają zróżnico-
wane potrzeby w tym względzie i nie dla każdego z nich dostęp do wszystkich operacji jest
potrzebny lub pożądany, przykładowo atrybuty i operacje związane z wewnętrznymi struk-
turami i mechanizmami klasy z oczywistych względów dostępne dla jej implementatora,

2
O d ang. integer — „całkowity" — przyp. tłum.
9.4. Aktywności specyfikowania interfejsów 405

public class Tournament {


private int maxNumPlayers;

public Tournament(League 1, int maxNumPlayers)


public int getMaxNumPlayers() { ... );
public List getPlayers() { ... };
public void acceptPlayer(Player p) { ... };
public void removePlayer(P1ayer p) { ... };
public boolean isPlayerAccepted(Player p) { ... };

Rysunek 9.3. Deklaracja klasy Tournament

niekoniecznie muszą być (a nawet nie powinny być) dostępne dla jej użytkownika. Z kolei dla
ekstendera klasy interesujące mogą być jedynie wybrane elementy zbioru jej wewnętrznych
atrybutów i operacji. Zróżnicowana dostępność poszczególnych atrybutów i operacji określana
jest mianem ich widzialności. W języku UML rozróżnia się cztery następujące poziomy
widzialności.

• Prywatny (pri vate) atrybut widoczny jest jedynie w obrębie implementacji swej
macierzystej klasy, podobnie prywatna operacja może być wywoływana wyłącznie
w ramach tej implementacji. Prywatne atrybuty i operacje przeznaczone są wyłącz-
nie dla implementatora klasy i nie są widoczne dla innych klas, nawet dla subklas
danej klasy; nie są zatem widoczne ani dla użytkownika tej klasy, ani dla jej ekstendera.
• Chroniony (protected) atrybut i chroniona operacja dostępne są zarówno w obrębie
implementacji swej macierzystej klasy, jak i w obrębie implementacji jej subklas.
Dla innych klas są niedostępne. Widoczne są zatem dla implementatora i ekstendera
klasy, ale nie dla jej użytkownika.
• Publiczny (publ i c) atrybut lub klasa dostępne są bez ograniczeń dla wszystkich klas.
Zbiór publicznych atrybutów i operacji danej klasy składa się na to, co nazywamy
jej publicznym interfejsem, przeznaczonym dla użytkowników.
• Pakietowy (package) atrybut lub operacja widoczne są bez ograniczeń w obrębie
wszystkich klas wchodzących w skład tego samego pakietu. Umożliwia to współdzie-
lenie atrybutów i operacji przez powiązane ze sobą klasy tworzące spójną całość
(na przykład podsystem), jednocześnie chroni przedmiotowe atrybuty i operacje
przed dostępem z zewnątrz do macierzystego pakietu.
406 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

W języku UML widzialność elementu (atrybutu lub klasy) oznaczana jest przez jedno-
znakowy przedrostek poprzedzający nazwę: minus (-) oznacza element prywatny (pri vate),
kratka (#) — element chroniony (protected), plus (+) — element publiczny (pub! ic), zaś tylda
(-) — element pakietowy (package). W diagramie na rysunku 9.3 atrybut maxNumPl ayers klasy
Tournament jest jej atrybutem prywatnym, podczas gdy wszystkie jej operacje są publiczne.
Typ atrybutu nie zawiera jednak kompletnej informacji na temat dopuszczalnego zbioru
wartości, jakie atrybut ten może przyjmować; przykładowo typ i nt atrybutu maxNumPl ayers
zezwala na nadawanie mu wartości ujemnych, bezsensownych jednak z punktu widzenia
dziedziny aplikacyjnej. Ten problem rozwiązywany jest za pomocą mechanizmu zwanego
kontraktem.

9.3.3. Kontrakty: niezmienniki, warunki wstępne i warunki końcowe


Kontraktem nazywamy zbiór ograniczeń narzuconych na daną klasę, który umożliwia jej
użytkownikom, implementatorom i ekstenderom współdzielenie dokładnie tych samych zało-
żeń [Meyer, 1997], Kontrakt obejmuje zarówno warunki konieczne do prawidłowego uży-
wania klasy, jak i warunki konieczne do spełnienia przez jej implementatorów i ekstenderów.
W kontrakcie zawarte są trzy typy ograniczeń:

• niezmienniki — to predykaty zawsze prawdziwe dla każdej instancji klasy, dotyczące


samej klasy lub jej interfejsu; każdy niezmiennik jest elementem zapewnienia spójności
między poszczególnymi atrybutami klasy,
• warunki wstępne — to predykaty związane z konkretną operacją; ich prawdziwość
przed wywołaniem tej operacji jest konieczna do prawidłowego jej wykonania;
warunki wstępne stanowią rodzaj zobowiązania dla użytkownika klasy,
• warunki końcowe — to predykaty, również związane z konkretną operacją, których
prawdziwość musi być gwarantowana po zakończeniu tej operacji, przy założeniu
spełnienia jej warunków wstępnych. Warunki końcowe stanowią rodzaj zobowiązania
dla implementatorów i ekstenderów klasy.

Jako przykładem posłużmy się interfejsem klasy Tournament z rysunku 9.3. Klasa ta posiada
metodę acceptPlayer() realizującą dodawanie nowego gracza (Player) do turnieju, metodę
removePl ayer() wycofującą gracza z turnieju i metodę getMaxNumPl ayers () zwracającą mak-
symalną dopuszczalną liczbę graczy w danym turnieju. Przykładem niezmiennika tej klasy
jest dodatnia wartość zwracana przez metodę getMaxNumPlayers(); gdyby była ona zerowa,
każde wywołanie metody acceptPl ayer () stanowiłoby naruszenie jej kontraktu i turniej repre-
zentowany przez instancję klasy Tournament nie mógłby się w ogóle rozpocząć. Niezmiennik
ten zapisać możemy w postaci wyrażenia boolowskiego:

t.getMaxNumPlayers() > 0
gdzie t jest instancją klasy Tournament.
Przykładem warunku wstępnego dla metody acceptPl ayer () jest wymaganie, by gracz
(PI ayer) stanowiący argument wywołania tej metody nie uczestniczył jeszcze w odnośnym
turnieju oraz by dla turnieju tego nie wyczerpano jeszcze maksymalnej liczby uczestników.
Oznaczając przez t instancję klasy Tournament reprezentującą wspomniany turniej, zaś
9.4. Aktywności specyfikowania interfejsów 407

przez p instancję klasy PI ayer reprezentującą gracza aplikującego do udziału w turnieju, wa-
runek ten możemy zapisać w postaci następującego wyrażenia boolowskiego:

!t.isPlayerAccepted(p) and t.getNumPlayersQ < t.getMaxNumPlayers()


Po poprawnym wykonaniu metody acceptPl ayer() liczba uczestników turnieju musi
być dokładnie o jeden większa niż przed jej wywołaniem, co uważać możemy za jej warunek
końcowy i zapisać w postaci kolejnego wyrażenia boolowskiego:
t.getNumPlayers_afterAccept = t.getNumPlayers__beforeAccept + 1

gdzie metody getNumPlayers_afterAccept i getNumPlayers_beforeAccept oznaczają


liczbę uczestników turnieju (odpowiednio) po wywołaniu metody i przed jej wywołaniem.
Niezmienniki, warunki wstępne i warunki końcowe wykorzystywane są do jednoznacz-
nego definiowania sytuacji specjalnych i wyjątkowych. Teoretycznie są one nawet wystarczające
do kompletnego określenia zachowania reprezentowanego przez daną operację — nazywa się
to „specyfikacją opartą na ograniczeniach" — w praktyce jednak sporządzenie takiej specyfi-
kacji jest trudne i bardziej skomplikowane niż implementacja samej operacji. W książce tej nie
będziemy więc wykorzystywać specyfikacji tego rodzaju, w zamian połączymy formalne defi-
nicje ograniczeń z opisami operacji w języku naturalnym i dokładnie przeanalizujemy możliwe
sytuacje wyjątkowe — takie rozwiązanie jest bardziej czytelne i zapewnia lepszą komunikację
między programistami.

9.3.4. Język OCL (Object Constraint Language)


Ograniczenia mogą być opisywane zarówno w języku naturalnym, jak i w postaci formalnej
przy użyciu odpowiedniego języka. Takim językiem jest OCL (Object Constraint Language)
[OMG, 2006] umożliwiający formułowanie ograniczeń zarówno w kategoriach pojedynczych
elementów modelu (atrybutów, operacji, klas), jak i grupowanie tych elementów (między in-
nymi skojarzeń między klasami czy uczestnictwa klas w skojarzeniu, dziedziczeniu i tym po-
dobnych). W tej i następnej sekcji przedstawimy podstawową składnię języka OCL; czytelni-
ków zainteresowanych jego dalszymi szczegółami odsyłamy do książki ]. Warmera i A. Kleppe
[Warmer i Kleppe, 2003].
Formalna definicja ograniczenia ma postać wyrażenia boolowskiego, czyli wyrażenia
zwracającego jedną z dwóch wartości: True albo Fal se. Na diagramach UML ograniczenia
mogą być reprezentowane w formie notatek przyporządkowywanych przedmiotowym ele-
mentom — w diagramie na rysunku 9.4 uwidocznione zostały opisane wcześniej ograniczenia
dotyczące klasy Tournament.
Ponieważ dołączanie wyrażeń OCL do diagramu może pogarszać jego czytelność, wyra-
żenia te mogą być formułowane także w formie tekstowej. Przykładowo warunek, by wartość
atrybutu maxNumPl ayers klasy Tournament była dodatnia, zapisać można w postaci następującej:

context Tournament inv:


self.getMaxNumPlayersO > 0
408 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

Rysunek 9.4. Przykłady niezmienników, warunków wstępnych i warunków końcowych, wyrażonych


w języku OCL i reprezentowanych w formie notatek na diagramie UML

Słowo kluczowe context wskazuje encję, do której odnosi się ograniczenie, po czym na-
stępuje określenie rodzaju graniczenia w postaci jednego z trzech słów kluczowych: i nv (nie-
zmiennik, od invariant), pre (warunek wstępny, od precondition) albo post (warunek końco-
wy, od postcondition). Odpowiednikami tych słów kluczowych na diagramie UML są stereotypy
(odpowiednio) «invariant», «precondition» i «postcondition». Potem następuje odpowied-
nie wyrażenie języka OCL. Składnia języka OCL podobna jest do składni języków zoriento-
wanych obiektowo, takich jak Java czy C++. Jednak w przeciwieństwie do nich język OCL
nie jest językiem proceduralnym — operacje mogą być używane w jego wyrażeniach, pod wa-
runkiem że nie powodują efektów ubocznych.
Dla niezmienników kontekstem wyrażenia jest klasa, której one dotyczą. Instancje tej
klasy reprezentowane są w wyrażeniach przez słowo kluczowe sel f 3 . Atrybuty i operacje iden-
tyfikowane są za pomocą kwalifikowanej notacji „kropkowej", na przykład sel f.maxNumPl ayers
oznacza wartość atrybutu maxNumPl ayers dla instancji łdasy stanowiącej kontekst ograniczenia.
Słowo sel f można pominąć, jeśli to nie prowadzi do niejednoznaczności.
Dla warunku wstępnego i warunku końcowego kontekstem wyrażenia OCL jest opera-
cja; parametry przekazywane do tej operacji mogą być używane jako zmienne w tym wyra-
żeniu. Przykładowo ograniczenie w postaci:

context T o u r n a m e n t : : a c c e p t P ł a y e r ( p : P l a y e r ) pre:
lisPlayerAccepted(p)

specyfikuje warunek wstępny, na mocy którego gracz, reprezentowany przez parametr p


operacji acceptPl ayer (), nie może być już uczestnikiem turnieju, do którego właśnie się
zgłasza. Wspomniany parametr p występuje jako zmienna w wyrażeniu formułującym warunek.
Jeśli dla danego kontekstu definiuje się lalka warunków wstępnych, wszystkie one muszą być
spełnione przed wywołaniem operacji. Do powyższego warunku wstępnego możemy na przy-
kład dorzucić następny stanowiący, by w turnieju tym nie został już wyczerpany limit liczby
uczestników:

context T o u r n a m e n t : : a c c e p t P l a y e r ( p : P l a y e r ) pre:
getNumPlayers() < getMaxNumPlayers()

Stanowiące odpowiednik słowa kluczowego t h i s w językach C++ i Java.


9.4. Aktywności specyfikowania interfejsów 409

Warunki końcowe zapisywane są w identyczny sposób, z tą różnicą, że zamiast słowa


kluczowego pre występuje słowo post oznaczające, że wartości wyrażeń ewaluowane są po
wykonaniu operacji. Przykładowo efektem wykonania operacji acceptPl ayer () dla gracza p
jest uczestnictwo tegoż gracza w turnieju:

context T o u r n a m e n t : : a c c e p t P l a y e r ( p : P I a y e r ) post:
isPlayerAccepted(p)

Jako że wyrażenia będące warunkami końcowymi ewaluowane są po zakończeniu ope-


racji będącej kontekstem ograniczenia, powstaje problem dostępności wartości (atrybutów
i operacji) sprzed jej wykonania — jeżeli przykładowo żądamy, by w wyniku wywołania ope-
racji acceptPl ayer () liczba uczestników turnieju była o jeden większa niż przed jej wy-
wołaniem, powinniśmy mieć dostęp do wartości zwracanej przez operację getNumPlayersQ
zarówno przed wywołaniem, jak i po zakończeniu operacji acceptPl ayer(). Problem ten roz-
wiązuje się w języku OCL za pomocą przedrostka @pre:

context T o u r n a m e n t : : a c c e p t P l a y e r ( p : P l a y e r ) post:
getNumPlayersQ = self@pre. getNumPlayersQ + 1

self@pre.getNumPlayers() oznacza tu wartość, jaką metoda getNumPlayersQ zwracała


przed wywołaniem operacji będącej kontekstem ograniczenia (acceptPl ayer()), zaś getNum
"-•PlayersO (bez przedrostka) oznacza wartość zwracaną przez nią po zakończeniu operacji
stanowiącej kontekst ograniczenia. Podobnie jak w przypadku warunków wstępnych, także
warunków końcowych może być kilka — wszystkie one muszą być spełnione.
Na podobnej zasadzie sformułować możemy warunki wstępne i końcowe, składające się
na kontrakt operacji removePl ayer ():

context Tournament::removePlayer(p:PIayer) pre:


isPJayerAccepted(p)
context T o u r n a m e n t : : r e m o v e P l a y e r ( p : P I a y e r ) post:
lisPlayerAccepted(p)
context T o u r n a m e n t : : r e m o v e P l a y e r ( p : P I a y e r ) post:
getNumPlayersQ = seJf@pre.getNumPlayersQ - 1

Ograniczenia OCL zapisywane są i wykorzystywane przez programistów na etapie pro-


jektowania obiektów i na etapie ich implementowania. Dla programów tworzonych w języku
Java dostępne są narzędzia w rodzaju iContract opisane przez R. Kramera [Kramer, 1998], które
umożliwiają dokumentowanie ograniczeń wprost w kodzie źródłowym, za pomocą znaczników
Javadoc, dzięki czemu ograniczenia te są łatwo dostępne i modyfikowalne. Przykład kodu
opatrzonego taką dokumentacją widoczny jest na listingu 9.1.
410 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

Listing 9.1. Deklaracje metod klasy Tournament opatrzone adnotacjami dotyczącymi warunków
wstępnych, warunków końcowych i niezmienników, zapisanymi przy użyciu znaczników Javadoc

/ ** Turniej (Tournament) jest serię meczów (Match) rozgrywanych między grupami graczy
* (Player) i kończęcych się wyłonieniem jednego zwycięzcy. Reguły gry i styl rozgrywania
* turnieju określone są przez obiekty będęce specjalizacjami klas Game i TournamentStyle
* skojarzonymi z klasę Tournament.
V

public class Tournament {


/** maksymalna liczba graczy uczestniczęcych w turnieju musi być zawsze dodatnia.
* @invariant maxNumPlayers > 0
7
private int maxNumPlayers;

/** List jest listę graczy (w postaci referencji do obiektów Player)


* uczestniczęcych w turnieju
7
private L i s t players;

.... konstruktor....

/** Zwraca liczbę graczy aktualnie uczestniczęcych w turnieju


7
public int g e t N u m P l a y e r s ( ) { ... }

/** Zwraca maksymalnę liczbę graczy uczestniczęcych w turnieju.


7
public int getMaxNumPlayers() { ... }

/** Dla operacji acceptPlayerQ zakładamy, że gracz reprezentowany


* przez parametr jej wywołania nie jest jeszcze uczestnikiem turnieju
* @pre lisPlayerAccepted(p)
* @pre getNumPlayersQ < maxNumPlayers
* @post isPlayerAccepted(p)
* @post getNumPlayersQ = self@pre.getNumPlayers() +1
V
public void a c c e p t P l a y e r ( P l a y e r p) { . . . }

/** Dla operacji removePlayer() zakładamy, że gracz reprezentowany


* przez parametr jej wywołania jest aktualnie uczestnikiem turnieju
* @pre isPlayerAccepted(p)
* @post HsPlayerAccepted(p)
* @post getNumPlayersQ = self@pre.getNumPlayers() -1
V

public void r e m o v e P l a y e r ( P I a y e r p) { . . . }

... pozostałe metody...


)
9.4. Aktywności specyfikowania interfejsów 411

9.3.5. Kolekcje OCL: zbiory, wielozbiory i ciągi


Ogólnie ograniczenia mogą obejmować dowolną liczbę klas i atrybutów. Na rysunku 9.5
widoczny jest model klas uwidaczniający skojarzenia między klasami League, Tournament
i PI ayer. Chcielibyśmy ten model udoskonalić, wprowadzając doń następujące ograniczenia:

1. Czas trwania turnieju (Tournament) nie może przekraczać tygodnia,


2. Gracze (PI ayer) mogą uczestniczyć w turnieju (Tournament) tylko wtedy, gdy za-
rejestrowani są w lidze (League) organizującej ten turniej,
3. Aktywnymi graczami ligi są ci, którzy wzięli udział w chociaż jednym turnieju
organizowanym przez tę ligę.

Rysunek 9.5. Skojarzenia między klasami League, Tournament i PI ayer systemu ARENA

Aby lepiej zrozumieć powyższe ograniczenia, rozważmy przykładową konfigurację in-


stancji wymienionych klas, widoczną na rysunku 9.6. W lidze t t t E x p e r t : League zarejestro-
wanych jest czworo graczy (PI ayer): al i ce, bob, marc i joe. Liga ta organizuje dwa turnieje:
wi nter:Tournament i xmas:Tournament; w pierwszym z nich uczestniczą al i ce i bob, w dru-
gim — bob, marc i joe. Do ligi chessNovi ce: League należy tylko jeden gracz — zoe; liga ta nie
organizuje żadnych turniejów. Zobaczmy, jak wygląda to na wspomnianym rysunku.

1. Turniej wi nter :Tournament trwa dwa dni, turniej xmas :Tournament trzy dni — czyli
oba poniżej tygodnia.
2. Wszyscy gracze (PI ayer) uczestniczący w turnieju wi nter: Tournament, a także wszyscy
uczestnicy turnieju xmas:Tournament, zarejestrowani są w lidze tttExpert:League.
Gracz zoe nie jest jednak członkiem ligi t t t E x p e r t : League i nie uczestniczy w żad-
nym turnieju.
412 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

Rysunek 9.6. Przykład ograniczeń obejmujący dwie ligi, dwa turnieje i pięcioro graczy

3. W lidze t t t E x p e r t : League jest czworo aktywnych graczy; liga chessNovi ce nie ma


aktywnych graczy, bowiem jedyny jej członek — zoe — nie uczestniczy w żadnym
turnieju.

Powyższe ograniczenia już na pierwszy rzut oka wyglądają odmiennie: pierwsze z nich
ogranicza się do jednej klasy, drugie obejmuje trzy klasy (PI ayer, Tournament, League) i sko-
jarzenia między nimi, przedmiotem trzeciego jest zbiór meczów (Match) powiązanych z okre-
ślonym turniejem. W każdym z tych przypadków startujemy od konkretnej klasy i nawigujemy
po innych klasach modelu. W ogólnym przypadku nawigacja ta obejmować może trzy różne
zakresy (co schematycznie przedstawiono na rysunku 9.7):

• lokalne atrybuty — czyli atrybuty pojedynczej klasy, takie jak data rozpoczęcia ( s t a r t )
i zakończenia (end) turnieju (Tournament),
• klasy bezpośrednio powiązane — nawigacja odbywa się wyłącznie na jednym pozio-
mie skojarzenia (na przykład skojarzenia graczy z turniejami lub skojarzenia ligi
z turniejem),
• klasy powiązane pośrednio — nawigacja obejmuje kilka poziomów skojarzeń (na
przykład skojarzenie ligi z graczami biorącymi udział w co najmniej jednym turnieju
organizowanym przez tę ligę).

Wyróżniliśmy owe trzy podstawowe typy nawigacji, bowiem każde ograniczenie w ję-
zyku OCL da się wyrazić za ich pomocą. Na podstawie treści poprzedniej sekcji możemy już
z łatwością zapisać pierwsze z przedstawionych ograniczeń — ograniczenie maksymalnego
czasu trwania turnieju do tygodnia.
9.4. Aktywności specyfikowania interfejsów 413

Rysunek 9.7. Trzy podstawowe typy nawigacji — za pomocą ich kombinacji można formułować
wszelkie ograniczenia w języku OCL

context Tournament inv:


self.end - s e l f . s t a r t < 7

W przypadku drugiego ograniczenia — członkostwo ligi jako warunek uczestnictwa


w turnieju — sprawa nie jest już tak prosta, bowiem skojarzenie graczy (Player) z ligami
(League) ma krotność „wiele na wiele", zatem ograniczenie dotyczy wielu obiektów. Sytuacja
taka obsługiwana jest w języku OCL przez kolekcje danych, które dzielą się na trzy typy:

• zbiory (set) — używane przy nawigowaniu wzdłuż pojedynczego skojarzenia. Przy-


kładowo nawigacja wzdłuż skojarzeń łączących turniej wi n t e r : Tournament z jego
uczestnikami daje w wyniku zbiór {al i ce, bob}, zaś nawigacja wzdłuż skojarzeń łą-
czących ligę tttExpert:League z jej członkami daje zbiór wynikowy {alice.bob,
marc, joe}. Należy jednak pamiętać, że wynikiem nawigacji po skojarzeniu o krot-
ności 1 jest pojedynczy obiekt, a nie zbiór obiektów: przykładowo nawigacja od
turniejów winter:Tournament i xmas:Tournament w kierunku organizującej je ligi
daje w wyniku obiekt t t t E x p e r t : League, a nie zbiór { t t t E x p e r t : League}.
• ciągi (sequence) — używane przy nawigowaniu wzdłuż pojedynczego uporządkowa-
nego skojarzenia. Takim uporządkowanym skojarzeniem jest na przykład skojarzenie
między klasami League i Tournament; rezultatem nawigowania po tym skojarzeniu
jest ciąg [winter:Tournament, xmas:Tournament]. Elementy ciągu mogą być identy-
fikowane przez indeksy, pierwszy element ma indeks 1, drugi 2 i tak dalej.
• wielozbiory (bag) — w przeciwieństwie do zbioru, elementy wielozbioru mogą się
powtarzać, dany element może wielokrotnie należeć do wielozbioru. Wielozbiory są
wynikiem akumulowania obiektów osiągalnych za pomocą skojarzeń pośrednich.
Przykładowo w celu określenia aktywnych członków ligi musimy „odwiedzić" wszyst-
kie turnieje organizowane przez tę ligę i dla każdego turnieju pobrać listę aktywnych
graczy. Dla ligi t t t E x p e r t da to w rezultacie wielozbiór {al ice, bob, bob, marc,
joe}, dla ligi chessNovice wynikiem takiej iteracji będzie (wielo)zbiór pusty.
414 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

W sytuacjach, gdy istotny jest jedynie fakt przynależności danego obiektu do wielo-
zbioru, bez względu na krotność tej przynależności, wielozbiór można łatwo skon-
wertować na zwykły zbiór za pomocą operacji asSet () (patrz tabela 9.1).

Operowanie na kolekcjach OCL umożliwiają różne operacje udostępniane przez ten język;
najczęściej używane z nich przedstawiamy w tabeli 9.1.

Tabela 9.1. Wybrane operacje kolekcji OCL

Operacja Zwracana wartość


size Liczba obiektów kolekcji.
i ncl udes(oóie/ri) True, gdy obiekt należy do kolekcji, False w przeciwnym razie.
select(wyrażenie) Kolekcja złożona z tych elementów oryginalnej kolekcji, dla których
wyrażenie ma wartość True.
u n i o n ( k o l e kej a) Suma teoriomnogościowa dwóch kolekcji — oryginalnej i kolekcji
stanowiącej p a r a m e t r wywołania: o b e j m u j e elementy należące
do przynajmniej jednej z obu kolekcji.
i ntersecti on(kolekcja) Iloczyn teoriomnogościowy dwóch kolekcji — oryginalnej i kolekcji
stanowiącej parametr wywołania: obejmuje elementy należące do obu
kolekcji równocześnie.
a s S e t ( k o l e kej a) Zbiór zawierający wszystkie elementy obecne w kole kej i stanowiącej
parametr wywołania.

W języku OCL odwołania do atrybutów różnią się od odwołań do kolekcji: pierwsze mają
postać notacji „kropkowej", drugie używają operatora ->. Przykładowo drugie ze sformuło-
wanych wcześniej wymagań — dostępność danego turnieju wyłącznie dla członków ligi orga-
nizującej ten turniej — można wyrazić następująco 4 :

context T o u r n a m e n t : : a c c e p t P l a y e r ( p : P l a y e r ) pre:
league.piayers->includes(p)

Zwróćmy uwagę na sposób identyfikowania klas League i PI ayer skojarzonych z klasą


Tournament, której operacja stanowi kontekst warunku: zgodnie z zasadami języka OCL,
klasy takie identyfikowane są przez nazwy prowadzących do nich skojarzeń, a jeżeli któreś ze
skojarzeń nie posiada nazwy, klasa identyfikowana jest przez swą własną nazwę, pisaną jednak
z małej litery. Wychodząc z klasy Tournament (stanowiącej element kontekstu) docieramy
najpierw do klasy League (identyfikowanej jako 1 eague — skojarzenie nie posiada nazwy), po
czym na podstawie jej agregacji z graczami (PI ayer), będącymi jej członkami, otrzymujemy
kolekcję pl ayers (wspomniane skojarzenie również nie jest nazwane). Dla tej kolekcji wy-
wołujemy operację i ncl udes (), parametr wywołania jest ten sam, co w operacji stanowią-
cej kontekst warunku.

4
Choć autorzy nie piszą tego wyraźnie, z treści i późniejszych rysunków wynika, ze p l a y e r s jest
tutaj nazwą skojarzenia — przyp. tłum.
9.4. Aktywności specyfikowania interfejsów 415

Poniższy przykład stanowi natomiast ilustrację użycia operacji a s S e t ( ) . Modyfiku-


jąc nieco założenie nr 3, załóżmy, że chcemy w klasie League jako wynik (resul t) operacji
League: :getActivePlayers:Set uzyskiwać zbiór aktywnych graczy w tej lidze. Kolekcjo-
nowanie w tym celu graczy uczestniczących w poszczególnych turniejach organizowanych
przez tę ligę to dopiero połowa roboty, bowiem dany gracz może się wielokrotnie znaleźć
w wynikowym wielozbiorze — „wielozbiorze", bo wynikiem iterowania dwóch łub więcej
skojarzeń typu „jeden na wiełe" lub „wiele na wiele" jest zawsze wiełozbiór. Pożądany wynik
otrzymamy dopiero po jego skonwertowaniu na zwykły zbiór5:

context L e a g u e : : g e t A c t i v e P l a y e r s : S e t post:
result = tournaments.players->asSet()

9.3.6. Kwantyfikatory OCL: forAll() i exists()


W uzupełnieniu do operacji opisanych w tabeli 9.1, język OCL udostępnia także dwie operacje
o charakterze kwantyfikatorów, opisane w tabeli 9.2.

Tabela 9.2. Kwantyfikatory języka OCL

Kwantyfikator Zwracana wartość


forAl1(zmienna:kolekcja\wyrażeń ie) True, jeżeli wyrażenie ma wartość True w wyniku
podstawienia pod zmi enną dowolnego elementu ko l ekcj i.
exists {zmienna -.kolekcja] wyrażeń i e) True, jeżeli w kolekcji istnieje przynajmniej jeden element
taki, że wyrażenie zwraca wartość True w wyniku jego
podstawienia pod zmienną.

Gdy na przykład chcemy się upewnić, że wszystkie mecze w ramach danego turnieju
rozpoczynają się i kończą w zakreślonych dla tego turnieju ramach czasowych, możemy sfor-
mułować następujący niezmiennik:

context Tournament inv:


matches->forAl1(m:Match | !m.start.before(start) and Im.end.after(end))

Zatem ogólnie rzecz biorąc, kwantyfikator forAl 1 () weryfikuje spełnienie pewnego


warunku przez wszystkie elementy kolekcji, podczas gdy kwantyfikator e x i s t s ( ) weryfikuje
istnienie w kolekcji elementu (co najmniej jednego) spełniającego pewien warunek. Gdy na
przykład chcemy mieć pewność, że w każdym turnieju w pierwszym dniu zostanie rozegrany
co najmniej jeden mecz, formułujemy następujące ograniczenie:

context Tournament inv:


matches->exists(m:Match | m . s t a r t . e q u a l s ( s t a r t ) )

5
Podobnie i tutaj tournaments i pl ayers są nazwami odnośnych skojarzeń — przyp. tłum.
416 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

9.4. Aktywności specyfikowania interfejsów


Na specyfikowanie interfejsów składają się następujące aktywności:

• Identyfikowanie brakujących atrybutów i operacji (patrz sekcja 9.4.1),


• Specyfikowanie typów, sygnatur i widzialności (patrz sekcja 9.4.2),
• Specyfikowanie warunków wstępnych i warunków końcowych (patrz sekcja 9.4.3),
• Specyfikowanie niezmienników (patrz sekcja 9.4.4),
• Dziedziczenie kontraktów (patrz sekcja 9.4.5).

Zilustrujemy je na przykładzie modelu projektu obiektów systemu ARENA, a konkretnie


na przykładzie obiektów uczestniczących w przypadku użycia AnnounceTournament (patrz
sekcja 5.6). W czasie analizy wymagań zidentyfikowaliśmy kilka klas brzegowych, sterują-
cych i encji — i tak na przykład klasa TournamentForm odpowiedzialna jest za generowanie
i przetwarzanie wszystkich formularzy składających się na interfejs użytkownika, zaś klasa
TournamentControl — za koordynowanie wszystkich transakcji odbywających się między
klasą TournamentForm a klasami encji Tournament, PI ayer i Match. Na rysunku 9.8 przed-
stawiono atrybuty, operacje i skojarzenia tych klas zidentyfikowane na etapie analizy wy-
magań. Uzupełnianie widocznego na rysunku modelu rozpoczniemy od zidentyfikowania
brakujących atrybutów i operacji.

Rysunek 9.8. Obiekty modelu analitycznego systemu ARENA zidentyfikowane w ramach analizy przypadku
użycia AnnounceTournament. Dla uproszczenia pominięto niektóre elementy
9.4. Aktywności specyfikowania interfejsów 417

9.4.1. identyfikowanie brakujących atrybutów i operacji


Przeanalizujemy teraz opis usług świadczonych przez poszczególne podsystemy systemu ARENA
i spróbujemy zidentyfikować brakujące atrybuty i operacje. To, że w czasie analizy wymagań
nie uwzględniliśmy wielu atrybutów, wynika z faktu, że koncentrowaliśmy się wówczas na
funkcjonalności systemu, którą opisywaliśmy głównie w formie modelu przypadków użycia
(w przeciwieństwie do operacji modelu projektu obiektów). Kierując się wiedzą z dziedziny
aplikacyjnej, podczas konstruowania modelu analitycznego ignorowaliśmy po prostu detale
systemu niemające związku z tą dziedziną.
Jedną z rzeczy, które uświadomiliśmy sobie już na początku projektowania obiektów
systemu ARENA, był fakt, iż liczba równolegle rozgrywanych turniejów i meczów stanowi
podstawowy czynnik wpływający na obciążenie zasobów tegoż systemu i prowadzący do po-
wstania „wąskiego gardła" przepustowości. Stało się jasne, że nie przewidzieliśmy możliwości
nadużyć ze strony graczy, którzy chcieliby rozgrywać kilka meczów równocześnie (oczywiście,
każdy mecz w innym turnieju). Po negocjacjach z ldientem zgodziliśmy się, że taka sytuacja
nie sprzyja ani serwerowi (który doznaje silnego obciążenia), ani graczowi (który zamiast
skupić się na jednym meczu, rozprasza swą uwagę na kilka, co może negatywnie odbić się na
jakości gry), ani operatorowi czerpiącemu zyski z reklam (wielokrotne wysyłanie tych samych
banerów na ten sam komputer w tym samym czasie nie zwiększa opłaty sponsoringowej).
W zamian można by zaproponować graczowi opcję sekwencyjnego uczestniczenia w po-
szczególnych turniejach, która likwiduje wszystkie opisane niedostatki.
Aby więc zapobiec uczestnictwu gracza w dwóch różnych turniejach zachodzących na
siebie czasowo, narysowaliśmy najpierw diagram sekwencji reprezentujący związany z tym
przepływ sterowania i danych (patrz rysunek 9.9). Tworzenie tego diagramu uświadomiło nam
brak operacji i sPl ayerOverbooked() sprawdzającej, czy gracz zgłaszający udział do danego
turnieju nie jest już uczestnikiem innego turnieju odbywającego się w tym samym czasie.

Rysunek 9.9. Diagram sekwencji dla operacji applyForTournament(), prowadzący do zidentyfikowania


brakującej operacji i sPl ayerOverbooked () wykrywającej uczestnictwo gracza w dwóch (lub więcej)
turniejach równocześnie
418 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

Ponieważ operacja isPlayerOverbooked() wymusza zachowanie reguł, które ostatnio


zidentyfikowaliśmy, a więc związana jest z organizacją turnieju, a nie z konkretnym graczem
czy konkretnym turniejem, przeto powierzyliśmy ją klasie TournamentControl. Dzięki temu
model obiektów encji stał się prostszy i łatwiej modyfikowalny. Przykładowo kolejne reguły
organizacyjne, jakich zażądać może klient (uczestnictwo gracza w co najwyżej jednym turnieju
w ciągu tygodnia czy zapobieganie udziałowi nieletnich graczy w późnych godzinach wie-
czornych i nocnych), mogą być implementowane wyłącznie w drodze modyfikacji klasy
TournamentControl, bez naruszania klas encji.
I tak oto zidentyfikowaliśmy dodatkową operacją związaną z przypadkiem użycia
Appl yForTournament i nie jest tajemnicą, że ta modyfikacja modelu projektu obiektów nie jest
ostatnia — analiza kolejnych podsystemów i przypadków użycia na pewno doprowadzi do
wielu kolejnych.

9.4.2. Specyfikowanie typów, sygnatur i widzialności


Teraz przystąpimy do specyfikowania typów atrybutów, sygnatur operacji i zakresów widocz-
ności poszczególnych atrybutów i operacji. Specyfikowanie typów prowadzi do ulepszenia
modelu projektu obiektów pod dwoma względami. Po pierwsze, konkretyzując typ atrybutu,
wybieramy zakres wartości, jakie może on przyjmować: gdy przykładowo określamy typ atry-
butów wyznaczających chwilę rozpoczęcia i zakończenia turnieju, decydujemy się na kon-
kretną granulację pomiaru czasu, czyli dokładność, z jaką czas ten będzie kontrolowany przez
aplikację. Przyjmując granulację na poziomie poszczególnych sekund (czyli czasu dnia), da-
jemy kapitanom lig możliwość organizowania wielu turniejów w ciągu jednego dnia, kiedy
przyjmiemy granulację na poziomie dni (czyli dat), pozbawiamy ich tej możliwości.
Po drugie, specyfikowanie typów atrybutów prowadzi do odwzorowywania klas i atry-
butów modelu we wbudowane typy oferowane przez środowisko programistyczne. Wybierając
na przykład typ Stri ng języka Java do reprezentowania atrybutu name klas League i Tournament,
sprawiamy, że dostępne dla tego atrybutu stają się wszystkie operacje, jakie Java oferuje dla
danych tego typu.
Przy okazji konfrontujemy też klasy naszego modelu z klasami oferowanymi przez
istniejące komponenty. W pakiecie J a v a . u t i 1 na przykład znajduje się kilka klas implemen-
tujących kolekcje elementów — i tak interfejs Li st dostarcza środki do operowania kolekcjami
uporządkowanymi, bez względu na ich fizyczną realizację, a interfejs Map oferuje mechanizm
mapowania tablicy unikalnych kluczy w dowolny zbiór encji. Wybierzemy interfejs Li s t do
reprezentowania kolekcji obiektów, na przykład kolekcji graczy uczestniczących w danym
turnieju, zaś interfejs Map posłuży do odwzorowań między obiektami, na przykład odwzoro-
wywania poszczególnych graczy w ich punktacje.
Na zakończenie określimy widzialność każdego atrybutu i każdej operacji; w tym celu
podzielimy atrybuty na dwie grupy: te, które powinny być publicznie dostępne, i te, do któ-
rych dostęp możliwy będzie wyłącznie za pośrednictwem metod klasy. Na podobnej zasadzie
dokonamy rozróżnienia między operacjami wchodzącymi w skład interfejsu klasy a opera-
cjami będącymi metodami użytkowymi klasy na jej wewnętrzne potrzeby. W przypadku klas
9.4. Aktywności specyfikowania interfejsów 419

abstrakcyjnych i klas przeznaczonych dó specjalizowania zidentyfikujemy także operacje chro-


nione, przeznaczone na użytek tworzonych subklas. Na rysunku 9.10 widoczny jest efekt wzboga-
cenia modelu z rysunku 9.8 o typy atrybutów, sygnatury operacji i zakresy widzialności.

Rysunek 9.10. Wzbogacenie modelu projektu obiektów systemu ARENA o specyfikację typów, sy-
gnatur i widzialności. Dla przejrzystości pominięto niektóre elementy

Mając za sobą sprecyzowanie typów, sygnatur i widoczności, zajmiemy się specyfiko-


waniem zachowania poszczególnych klas i przypadkami granicznymi tych zachowań. Temu
celowi służy definiowanie kontraktów.

9.4.3. Specyfikowanie warunków wstępnych i warunków końcowych


Jak już wspominaliśmy, kontrakt dla danej klasy jest wyrazem porozumienia jej implementa-
tora (implementatorów) z użytkownikami. Warunki wstępne dla wszystkich operacji są czę-
ścią kontraktu wiążącą użytkownika klasy, natomiast warunki końcowe stanowią zobowiązanie
dla implementatora, o ile użytkownik klasy dotrzyma wspomnianych warunków wstępnych.
Ekstender rozwijający klasę dziedziczy kontrakt po implementatorze tej klasy.
I tak na przykład w sekcji 9.4.1 zdefiniowaliśmy operację i sPl ayerOverbooked () klasy
TournamentControl, wykrywającą okoliczności wykluczające gracza z udziału w danym tur-
nieju. Jako implementatorzy przyjęliśmy milcząco założenie, iż weryfikowany gracz nie jest już
w tym turnieju zarejestrowany i że sprawdzanie nie odbywa się post factum (bo — oczywiście
420 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

— otrzymalibyśmy wtedy werdykt wykluczający — każdy turniej zachodzi przecież czasowo


sam na siebie). Zweryfikowaniem prawdziwości tego założenia obarczyliśmy użytkownika
klasy, co znajduje odzwierciedlenie w warunku wstępnym poniższego kontraktu:

/* działanie operacji isPlayerOverbooked opiera się na założeniu,


* że gracz nie jest już zarejestrowany w odnośnym turnieju
V
context T o u r n a m e n t C o n t r o l : : i s P l a y e r O v e r b o o k e d ( p ) pre:
not p.tournaments->includes(self.tournament)

/* Gracz nie może uczestniczyć w dwóch turniejach nakładających się czasowo


V
context T o u r n a m e n t C o n t r o l : : i s P l a y e r O v e r b o o k e d ( p ) post:
r e s u l t = p.tournaments->exists(t| t.overlaps(self.tournament))

Warunki wstępne i warunki końcowe mogą być także wykorzystywane do specyfikowania


zależności między operacjami tej samej klasy, a szczególnie żądanej kolejności ich wywoływania,
jak w przypadku klasy TournamentControl. Dla danego turnieju (Tournament) nie możemy
rozwiązać kwestii sponsorowania bez uprzedniego otrzymania listy reklamodawców zaintere-
sowanych sponsoringiem. Nie możemy ogłosić rozpoczęcia turnieju przed dokonaniem wy-
boru sponsora (lub rezygnacją kapitana ligi z takiego wyboru). Właściwą kolejność poszcze-
gólnych operacji określimy w formie zestawu warunków wstępnych i końcowych, badających
stan obiektu klasy Tournament.
I tak konieczność uzyskania listy reldamodawców przed wybraniem sponsora zapisać
możemy następująco:

context T o u r n a m e n t C o n t r o l : : s e l e c t S p o n s o r s ( a d v e r t i s e r s ) pre:
interestedSponsors->notEmpty()

Kolejny warunek chroni przed wielokrotnym wyborem sponsora dla turnieju — operacja
TournamentControl . s e l e c t S p o n s o r s ( ) może być wywołana tylko raz:

context T o u r n a m e n t C o n t r o l : : s e l e c t S p o n s o r s ( a d v e r t i s e r s ) pre:
tournament.sponsors->isEmpty()

Wreszcie, dla zweryfikowania prawidłowości kojarzenia turniejów ze sponsorami przez


metodę TournamentControl .sel ectSponsors (), dodamy następujący warunek końcowy:

context T o u r n a m e n t C o n t r o l : : s e l e c t S p o n s o r s ( a d v e r t i s e r s ) post:
tournament.sponsors.equals(adverti sers)

Poniżej przedstawiamy kompletny zbiór ograniczeń OCL, wymuszających właściwą ko-


lejność wywoływania operacji sel ectSponsors (), adverti seTournamentQ i acceptPl ayer ().
9.4. Aktywności specyfikowania interfejsów 421

/* Warunki wstępne i końcowe wymuszające właściwą kolejność wywoływania


* operacji klasy TournamentControl
7
context T o u r n a m e n t C o n t r o l : : s e l e c t S p o n s o r s ( a d v e r t i s e r s ) pre:
interestedSponsors->notEmpty() and tournament.sponsors->isEmpty()

context T o u r n a m e n t C o n t r o l : : s e l e c t S p o n s o r s ( a d v e r t i s e r s ) post:
tournament.sponsors.equals(adverti sers)

context T o u r n a m e n t C o n t r o l : : a d v e r t i s e T o u r n a m e n t ( ) pre:
tournament.sponsors->isEmpty() and not tournament.advertised

context T o u r n a m e n t C o n t r o l : : a d v e r t i s e T o u r n a m e n t ( ) post:
tournament.advertised

context T o u r n a m e n t C o n t r o l : : a c c e p t P l a y e r ( p ) pre:
tournament.advertised and
i nterestedPlayers->includes(p) and
not isPlayerOverbooked(p)

context T o u r n a m e n t C o n t r o l : : a c c e p t P l a y e r ( p ) post:
tournament.piayers->i ncludes(p)

9.4.4. Specyfikowanie niezmienników


Po opanowaniu składni i koncepcji języka OCL pisanie kontraktów dla poszczególnych ope-
racji wydaje się dość proste. Koncepcja kontraktu między implementatorem a użytkownikiem
jest intuicyjnie jasna („zapewniam ci to a to, pod warunkiem, że spełnisz to a to") i opiera się
na krótkich odcinkach czasu (czyli wykonywaniu poszczególnych operacji). Nie ułatwia to
jednak zrozumienia natury samej klasy i jej zasadniczych własności, bowiem związana z tym
informacja jest rozproszona na wiele warunków i wiele operacji. W tym kontekście istotnego
znaczenia nabierają niezmienniki klasy.
Niezmienniki formułuje się jednak trudniej niż warunki wstępne i końcowe, choćby
z tego względu, że odzwierciedlają one w bardziej bezpośredni sposób naturę samej klasy
— naturę zwykle skomplikowaną. Stanowią one długoterminowy kontrakt, rozszerzający,
a niekiedy przedefiniowujący kontrakty dotyczące poszczególnych operacji. Aktywności zwią-
zane z identyfikowaniem niezmienników podobne są do związanych z konstruowaniem klas
abstrakcyjnych na etapie analizy wymagań (patrz sekcja 5.4.10): niektóre z niezmienników są
wręcz oczywiste, niektóre dają się wydedukować z kontraktów dla poszczególnych operacji.
I tak na przykład w systemie ARENA oczywistym niezmiennikiem jest rozpoczynanie
i kończenie każdego meczu turnieju w założonych dla tego turnieju ramach czasowych:
422 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

context Tournament inv:


matches->forAl1(m|im.start.before(start) and im.end.after(end))

Niezmiennikiem mniej oczywistym, choć dającym się łatwo wydedukować z kontraktu


dla klasy TournamentControl, jest rozłączność czasowa dwóch różnych turniejów, w których
bierze udział ten sam gracz — innymi słowy, wśród wszystkich turniejów, w jakich dany gracz
uczestniczy, dowolne dwa różne są czasowo rozłączne. Mimo iż można wyczytać tę interesu-
jącą właściwość, analizując operację TournamentControl .isPlayerOverbookedQ, możemy
zapisać ją w sposób jawny jako niezmiennik:

context TournamentControl inv:


tournament.piayers->forAl1(p[p.tournaments->forAl 1(t|
t <> tournament implies not t.overlap(tournament)))

Gdy ograniczenia definiowane są na bazie wielu skojarzeń, stają się skomplikowane


i trudne do zrozumienia, zwłaszcza gdy zawierają zagnieżdżone kwantyfikatory forAl 1. Jako
przykład weźmy pod uwagę kolejny niezmiennik, którym jest zarejestrowanie w turnieju jako
warunek rozgrywania meczów w ramach tego turnieju:

W powyższym ograniczeniu występują trzy kolekcje: players, p. tournaments i t . matches.


Możemy jednak uprościć jego zapis, wykorzystując wielozbiór powstający w wyniku nawigo-
wania po szeregu skojarzeń:

context Match inv:


piayers.tournaments.matches.i ncl udes(sel f)

Podobne zabiegi redukujące liczbę operacji w zapisie ograniczeń są jak najbardziej po-
żądane z punktu widzenia czytelności tych ograniczeń.
Jak wynika z przytoczonych przykładów, stosunkowo łatwo generować obszerne nawet
zbiory ograniczeń dla poszczególnych klas. Łatwość ta nie zawsze jednak idzie w parze z czy-
telnością, bo formułowanie czytelnych i poprawnych ograniczeń takie łatwe już nie jest. Nie
zapominajmy, że celem artykułowania niezmienników jest wyeksponowanie założeń, jakie
implementator klasy przyjmuje względem jej użytkowników. Niezmienniki powinny zatem
9.4. Aktywności specyfikowania interfejsów 423

w p i e r w s z y m rzędzie o d z w i e r c i e d l a ć w y r a ź n e w a r u n k i g r a n i c z n e , k t ó r e p o d i n n ą p o s t a c i ą n i e
byłyby tak oczywiste. P o n i ż e j p r z e d s t a w i a m y kilka h e u r y s t y k , k t ó r e ułatwić m o g ą f o r m u ł o -
wanie ograniczeń w sposób czytelny i zrozumiały.

Heurystyki p o m o c n e w c z y t e l n y m f o r m u ł o w a n i u o g r a n i c z e ń

Skoncentruj się na czasie życia instancji danej klasy. Ograniczenia specyficzne dla operacji lub tylko
dla pewnych stanów obiektu najlepiej wyrażają się jako warunki wstępne lub końcowe, na przykład:
• „różni gracze m a j ą różne adresy e-maił" — to i n f o r m a c j a p e r m a n e n t n a o charakterze
niezmiennika,

• „dany gracz może być w danym momencie zarejestrowany w co najwyżej jednym z odbywających
się równolegle turniejów" — to warunek wstępny operacji Tournament Form. appl y ForTournament ().

Zidentyfikuj wartości specjalne dla każdego atrybutu. Wartość zero, wartość pusta, wartości
szczególne w danym zakresie oraz atrybuty zależne od innych atrybutów to najczęstsze źródła
nieporozumień i błędów, na przykład:
• w meczu musi brać udział co najmniej jeden gracz,
® rezultatem meczu jest wyłonienie dokładnie jednego zwycięzcy.

Zidentyfikuj szczególne przypadki skojarzeń, zwłaszcza te ich aspekty, których nie sposób wyra-
zić za pomocą krotności, na przykład:
• zbiór graczy uczestniczących w turnieju (tournament. pl ayers) jest podzbiorem zbioru
członków ligi organizującej ten turniej (tournament. 1 eague.pl ayers).

Zidentyfikuj wymaganą kolejność wykonywania poszczególnych operacji, jak w przypadku klasy


TournamentControl opisywanym w sekcji 9.4.3.

Wykorzystuj metody pomocnicze do hermetyzacji skomplikowanych obliczeń. W rezultacie otrzymasz


zarówno bardziej czytelne ograniczenia, jak i łatwiejszy w testowaniu kod programu. Przykładem ta-
kiej metody pomocniczej może być:
• Tournament .overl aps ( t : Tournament) sprawdzająca, czy dwa turnieje nie są rozłączne czasowo.

Unikaj ograniczeń związanych z trawersowaniem wielu skojarzeń. Formułowanie takich ograniczeń


zwiększa stopień zależności między zbiorami niepowiązanych klas, co — oczywiście — jest efektem
niepożądanym. Przykładowo specyfikując, iż długość trwania meczu nie może przewyższać wartości
granicznej ustalonej dla danej gry (właściwej temu meczowi), możemy tę wartość graniczą wy-
razić jako:
• match.tournament.1eague.game.maxMatchDurati on

Jeżeli jednak zdefiniujemy dla klasy Match operację pomocniczą getGame (), zwracającą obiekt sub-
klasy Game reprezentujący grę właściwą danemu meczowi, powyższe ograniczenie będziemy mogli
napisać w bardziej czytelnej postaci:

• match.getGame().maxMatchDuration

N i e z m i e n n i k i , w a r u n k i w s t ę p n e i w a r u n k i k o ń c o w e określają s e m a n t y k ę d a n e j operacji
p r z e z j a w n e w y a r t y k u ł o w a n i e o k o l i c z n o ś c i , k t ó r e m u s z ą w y s t ę p o w a ć p r z e d jej w y w o ł a n i e m
i p o jej z a k o ń c z e n i u . K o n t r a k t klasy s t a n o w i więc czytelną d o k u m e n t a c j e dla jej u ż y t k o w n i k a .
Jak za chwilę z o b a c z y m y , k o n t r a k t d a n e j klasy jest r ó w n i e ż i s t o t n y dla jej e k s t e n d e r a .
424 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

9.4.5. Dziedziczenie kontraktów


W języku programowania uwzględniającym polimorfizm klasa może być zastępowana przez
swe klasy pochodne. Oznacza to między innymi, że operacje zdefiniowane w superldasie mogą
być wywoływane zarówno na rzecz jej samej, jak i na rzecz jej subklas. Użytkownik danej klasy
ma zatem prawo oczekiwać, że obowiązujące w stosunku do niej kontrakty respektowane będą
także dla jej subklas. Zasadę tę nazywamy dziedziczeniem kontraktów.
Rozpatrzmy hierarchię dziedziczenia klas w systemie ARENA, widoczną na rysunku 9.11,
i załóżmy, że dla klasy User określony został niezmiennik w postaci reguły, iż dla każdego
użytkownika (User) powinien istnieć niepusty adres e-mail, dzięki czemu użytkownik ten bę-
dzie mógł być powiadamiany o zdarzeniach. Jeżeli teraz w dowolnym momencie projektu zde-
cydujemy, że kibic (Spectator) nie musi dostarczać adresu e-mail, kontrakt dla ldasy User
zostanie złamany przez klasę Spectator. By takiej sytuacji uniknąć, należy bądź to usunąć
klasę Spectator z drzewa subklas klasy User, bądź też zrezygnować ze wspomnianego nie-
zmiennika (prawdopodobnie na rzecz podobnego, traktującego o adresach e-mail).

Rysunek 9.11. Prosty przykład dziedziczenia kontraktu. Niezmiennik zdefiniowany dla superklasy
musi być zachowany w każdej z jej subklas

Ogólnie dziedziczenie kontraktów rządzi się trzema regułami, dotyczącymi kolejno:

• warunków wstępnych: przedefiniowywanie metody w subklasie może osłabiać zwią-


zane z nią warunki wstępne, bowiem funkcjonalność metody przedefiniowanej może
być bogatsza niż funkcjonalność metody oryginalnej. Przykładowo w kontrakcie dla
klasy SimpleKnockOutStyle wywodzącej się z klasy abstrakcyjnej TournamentStyle
znajduje się warunek wstępny dla wszystkich jej operacji, zgodnie z którym liczba gra-
czy zarejestrowanych w turnieju musi być potęgą liczby 2. W klasie pochodnej Compl ex
'-•KnockOutStyl e możemy ten warunek osłabić, dopuszczając dowolną liczbę graczy.
• warunków końcowych: przedefiniowana metoda musi zapewnić te same (lub bardziej
rygorystyczne) warunki końcowe, co metoda oryginalna. Załóżmy, że implementu-
jemy interfejs Set za pomocą dziedziczenia implementacyjnego po klasie Li st (patrz
sekcja 8.3.2), co — jak wiemy — nie jest praktyką zalecaną. Warunkiem końcowym
metody List.add() jest zwiększenie o 1 liczby elementów zawartych w kolekcji tej
klasy. Wywołanie metody Set. add () niekoniecznie musi zwiększać liczbę elementów
kolekcji6, klasa Set łamie więc kontrakt ustanowiony dla klasy List, co jest waż-
ną przesłanką na rzecz unikania dziedziczenia implementacyjnego.

6
Dodawanie do zbioru elementu już w nim obecnego nie wywołuje żadnej akcji, nie zwiększa zatem
liczebności zbioru — przyp. tłum.
9.5. Zarządzanie projektowaniem obiektów 425

• niezmienników: subklasa musi respektować wszystkie niezmienniki zdefiniowane


dla superklasy, co najwyżej definiując nowe. Przykładowo jednym z niezmienników
klasy Collection jest nieujemny rozmiar kolekcji. Klasa List, jako subklasa klasy
Collection, respektuje ten niezmiennik, dodając nowy, dotyczący uporządkowania
elementów.

Dziedziczenie kontraktów staje się szczególnie użyteczne przy definiowaniu klas abstrak-
cyjnych lub interfejsów podlegających na przedefiniowywaniu (implementowaniu) przez
ekstenderów. Precyzyjne zdefiniowanie granic między klasą kliencką a interfejsem umożliwia
ekstenderom implementowanie specjalizowanych subklas bez znajomości kodu źródłowego,
w którym będą wykorzystywane. Dziedziczenie kontraktów jest ponadto konsekwencją zasady
zastępowania Liskov (patrz sekcja 8.3.4), ponieważ każde wystąpienie klasy oryginalnej może
być zastąpione wystąpieniem jej subklasy.

9.5. Zarządzanie projektowaniem obiektów


Z zarządzaniem etapem projektowania obiektów wiążą się dwa podstawowe wyzwania.

• Rosnąca złożoność komunikacji. W tej fazie liczba uczestników zaangażowanych


w projekt zwiększa się pokaźnie. Model projektu obiektów i kod programu powstają
w wyniku współpracy wielu ludzi. Zadaniem menedżera jest zapewnienie spójności
podejmowanych decyzji i ich zgodności z celami projektowymi.
• Spójność z poprzednimi decyzjami i istniejącymi dokumentami. Tak to już jest, że pro-
gramiści nie doceniają należycie znaczenia decyzji podejmowanych na etapie analizy
wymagań i etapie projektowania systemu; gdy przychodzi do doskonalenia modelu
projektu obiektów, programiści mogą kwestionować wiele z tych decyzji bądź weryfi-
kować ich podjęcie. Menedżer odpowiedzialny jest wówczas za zapewnienie do-
kumentowania tych zdarzeń, tak by wszystkie dokumenty odzwierciedlały bieżący
stan rzeczy.

Zajmiemy się tymi wyzwaniami w sekcji 9.5.1, w której też opiszemy dokument projektu
obiektów (ODD) — jego tworzenie i utrzymywanie aktualności oraz relacje z innymi dokumen-
tami. W sekcji 5.9.1 omówimy natomiast role przypisywane uczestnikom w związku z projek-
towaniem obiektów.

9.5.1. Dokumentowanie projektowania obiektów


Etap projektowania obiektów dokumentowany jest w formie Dokumentu Projektu Obiektów,
w skrócie ODD (Object Design Document). W dokumencie tym odzwierciedlane są kompromisy
rozstrzygane przez programistów, wytyczne, jakimi kierowali się przy projektowaniu interfej-
sów poszczególnych podsystemów, podział (dekompozycja)7 podsystemów na pakiety i klasy
oraz interfejsy poszczególnych klas. Dokument ODD używany jest do wymiany informacji

7
Nie mylić z dekompozycją systemu na podsystemy, dokonywaną na etapie projektowania systemu
— przyp. tłum.
426 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

między zespołami oraz jako punkt odniesienia na etapie testowania. Odbiorcami dokumentu
ODD są między innymi architekci systemu (czyli programiści, którzy uczestniczyli w fazie
projektowania systemu), programiści implementujący podsystemy oraz testerzy.
Podczas budowania dokumentu ODD możemy skorzystać z trzech podejść. Oto one.

• Samoistny dokument ODD tworzony na podstawie modelu. Odwzorowywanie mo-


delu w dokument odbywa się tu tak samo jak na etapach analizy wymagań i projekto-
wania systemu: tworzymy model, pilnując jego aktualności, i na jego podstawie gene-
rujemy dokument w sposób automatyczny. W rezultacie w dokumencie ODD znajdzie
się także opis obiektów dziedziny aplikacyjnej, zidentyfikowanych na etapie analizy.
Wadą takiego rozwiązania jest duża redundancja, spowodowana powieleniem dużej
części dokumentu RAD i wynikający stąd spory wysiłek związany z utrzymywaniem
spójności obu dokumentów — RAD i ODD — w przypadku modyfikowania któregoś
z nich, jak również podczas zmiany kodu źródłowego.
• Dokument ODD budowany jako rozszerzenie dokumentu RAD. Z perspektywy tej
metody model projektu obiektów traktowany jest jak rozszerzenie modelu analitycz-
nego — czyli jak zbiór obiektów dziedziny aplikacyjnej wzbogacony o obiekty re-
alizacyjne. Pozbywamy się w ten sposób redundancji i ułatwiamy sobie utrzymywanie
spójności dokumentów ODD i RAD, ale też ryzykujemy wypełnienie dokumentu
RAD informacjami nieistotnymi dla klienta i użytkowników. Co więcej, projektowanie
obiektów wykracza zwykle daleko poza samo identyfikowanie „dodatkowych"
obiektów, jakimi są obiekty realizacyjne, wymaga bowiem często modyfikowania lub
transformowania obiektów aplikacyjnych w celu zapewnienia ich zgodności z celami
projektowymi lub sprostania wymogom wydajnościowym.
• Dokument ODD wbudowany w kod źródłowy aplikacji. Podobnie jak w pierwszym
przypadku, sam dokument ODD generowany jest za pomocą narzędzi modelowania
(patrz rysunek 9.12). Gdy tylko model projektowania obiektów okaże się w miarę
stabilny, dokonujemy na jego podstawie (automatycznego) generowania namiastek
(stubs) poszczególnych klas. Interfejs każdej klasy opisywany jest wówczas przy
użyciu znacznikowanych komentarzy, umożliwiających rozróżnienie „zwykłych"
komentarzy do kodu od opisów obiektów modelu. Tak wygenerowany kod możemy
poddać przetwarzaniu przez parser ekstrahujący istotną informację i w ten sposób
wygenerować dokument ODD. Dysponując modelem projektu obiektów osadzonym
integralnie w kodzie źródłowym programu, możemy zapomnieć o modelu projektu
obiektów w pierwotnej postaci. Zaletą tego rozwiązania jest łatwość utrzymywania
spójności między (osadzonym) modelem projektu obiektów a kodem źródłowym oraz
samym dokumentem ODD: gdy zmienia się kod źródłowy, uaktualnione zostają
znaczniki opisu, po czym na ich podstawie można wygenerować aktualny doku-
ment ODD.

Podstawowa trudność w projektowaniu obiektów wiąże się z utrzymywaniem spójności


między dwoma modelami i kodem źródłowym. Byłoby idealnie, gdybyśmy mogli utrzymywać
całą trójkę — model analityczny, model projektu obiektów i kod źródłowy — za pomocą jed-
nego narzędzia: każdy obiekt opisywany byłby wówczas w jednym miejscu, a spójność między
dokumentacją, namiastkami klas i kodem byłaby zapewniona automatycznie.
9.5. Zarządzanie projektowaniem obiektów 427

Rysunek 9.12. Wbudowywanie modelu projektu obiektów w kod programu. Model ten dokumentowany
jest w formie znacznikowanych komentarzy, na podstawie których generować można dokument O D D

Aktualnie jednak dysponujemy narzędziami opartymi na języku UML, które umożli-


wiają generowanie zarówno dokumentacji, jak i namiastek klas na podstawie modelu. Przy-
kładowo drogą kolekcjonowania opisów towarzyszących poszczególnym klasom można au-
tomatycznie tworzyć słowniki wchodzące w skład dokumentów RAD (patrz rysunek 9.12).
Generowanie namiastek klas, zwane inżynierią postępującą {forward, engineering), jest wtedy
punktem startowym tworzenia interfejsów poszczególnych klas i kodu poszczególnych metod.
Niektóre narzędzie do modelowania udostępniają technikę odwrotną — inżynierię od-
wracającą (reverse engineering), polegającą na regenerowaniu modelu na postawie kodu źródło-
wego. Technologia ta bywa pomocna przy tworzeniu modelu projektu obiektów dla istniejącego,
starszego kodu. Trudno jednak spodziewać się pełnego automatyzmu w tej mierze, technologia
wymaga sporo „ręcznej" pracy, choćby w związku z faktem, że nie jest możliwe odtwarzanie
dwukierunkowych skojarzeń na podstawie samych tylko atrybutów referencyjnych.
428 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

Oferta narzędzi modelujących kurczy się wydatnie, gdy żądamy obsługi zależności
w dwóch kierunkach, szczególnie zależności między modelem analitycznym a kodem źró-
dłowym. Niektóre narzędzia, między innymi Rationale Rose [Rational, 2002] i Together Control
Center [TogetherSoft, 2002], realizują to zadanie przez wbudowywanie w kod źródłowy
komentarzy zawierających informacje dotyczące modelu analitycznego, takich jak informacje
o skojarzeniach i innych konstrukcjach UML. Mimo iż umożliwia to śledzenie zmian syntak-
tycznych w kodzie programu, programiści wciąż muszą aktualizować opisy zawarte w modelu
w celu czytelnego uwzględniania dokonywanych zmian. W efekcie programista zmuszony jest
do używania dwóch różnych narzędzi, dla kodu źródłowego i dla modelu, nie jest więc nie-
spodzianką, że zwykle cierpi na tym model.
Dopóki więc nie pojawią się narzędzia oferujące lepszą obsługę utrzymywania spójności
między modelami obiektowymi a kodem źródłowym, najbardziej rozsądną metodologią wy-
daje się generowanie dokumentu ODD w oparciu o kod źródłowy i ograniczanie dokumentu
RAD wyłącznie do dziedziny aplikacyjnej. Redukuje to rozmiar redundantnej informacji, którą
utrzymywać trzeba w aktualnym stanie, w dalszym ciągu jednak spójność między kodem źró-
dłowym a modelem analitycznym musi być utrzymywana ręcznie. Nie jest to trudne zadanie,
bowiem zmiany w kodzie źródłowym wymagają w większości modyfikowania tylko doku-
mentu ODD, rzadziej dokumentu RAD.
Poniżej widoczny jest przykładowy szablon generowanego automatycznie dokumentu
ODD.

Dokument projektu obiektów


1. Wstęp
1.1. Kompromisy rozstrzygane w związku z projektowaniem obiektów
1.2. Wytyczne projektowania interfejsów
1.3. Definicje, akronimy i skróty
1.4. Odwołania
2. Pakiety
3. Interfejsy klas
4. Słownik

W pierwszej sekcji dokumentu ODD, mającej charakter wstępu, opisywane są ogólne


kompromisy, jakie rozstrzygać musieli programiści (zakup gotowych komponentów albo two-
rzenie własnych, optymalizacja czasu odpowiedzi kontra optymalizacja wykorzystania pamięci
i tym podobne), wskazówki i konwencje (między innymi konwencje nazewnicze, przypadki
graniczne, mechanizmy obsługi wyjątków), a także ogólny układ dokumentu.
Wytyczne projektowania interfejsów i konwencje kodowania to bodaj najważniejsze
czynniki mogące usprawnić komunikację między programistami projektującymi interfejsy
i opatrującymi ich elementy odpowiednimi nazwami. Oto przykłady wytycznych tego rodzaju.

• Nazwy klas mają postać rzeczowników w liczbie pojedynczej.


• Metody opatrywane są nazwami w postaci fraz czasownikowych, zaś pola i para-
metry — nazwami w postaci fraz rzeczownikowych.
9.5. Zarządzanie projektowaniem obiektów 429

• Sytuacje błędne sygnalizowane'są poprzez generowanie wyjątków, nie poprzez


zwracanie kodu błędu przez metody.
• Kolekcje i kontenery obowiązkowo posiadają metodę i t e r a t o r ( ) zwracającą
Iterator.
• Iteratory zwracane przez metody i t e r a t o r () są odporne na usuwanie elementów.

Takie i podobne konwencje pomagają programistom (nieraz bardzo wielu) projektować


interfejsy w sposób spójny. Gdy owe konwencje są jasne i wiadome jeszcze przed rozpoczę-
ciem projektowania obiektów, łatwe jest przystosowanie się do nich — jest przy tym oczy-
wiste, że powinny one pozostać niezmienne przez cały czas realizacji projektu. Przykład zale-
canych konwencji kodowania w języku Java znaleźć można w publikacji [Sun, 2009].
Druga część dokumentu ODD, poświęcona pakietom, opisuje dekompozycję pod-
systemów na pakiety oraz organizację plików zawierających kod źródłowy programu. Dla każ-
dego pakietu przewidziany jest ogólny opis, lista pakietów, od których jest zależny, i wskaza-
nie oczekiwanego sposobu jego używania.
W części trzeciej, dedykowanej interfejsom klas, opisywane są poszczególne klasy i ich
publiczne interfejsy. Opis ten zawiera ogólną charakterystykę każdej z klas, jej zależności od
innych klas i pakietów, jej publiczne atrybuty i operacje oraz wyjątki, jakie może generować.
Początkowa wersja dokumentu ODD może być sporządzona już wtedy, gdy ustabilizuje
się kształt dekompozycji systemu na podsystemy. Dokument ten będzie później aktualizowany
za każdym razem, gdy pojawi się nowy interfejs lub zmodyfikowany zostanie któryś z istnieją-
cych. Nawet jeżeli niektóre podsystemy nie będą jeszcze funkcjonalne, znajomość ich in-
terfejsów w kategoriach kodu źródłowego umożliwi programistom kodowanie podsystemów
od nich zależnych i komunikowanie się w sposób jednoznaczny, a przy okazji odkrycie bra-
kujących parametrów i nowych przypadków granicznych. Opracowywanie dokumentów
ODD różni się od opracowania dokumentów innego typu przede wszystkich większą liczbą
uczestników i większą dynamiką zmian. W celu sprostania konsekwencjom tej specyfiki
można wykorzystywać narzędzia automatyzujące generowanie drugiej i trzeciej części do-
kumentu. Dla programów w języku Java umożliwia to Javadoc — narzędzie generujące strony
W W W na podstawie znacznikowanych komentarzy towarzyszących kodowi źródłowemu.
Na listingu 9.2 widoczny jest przykład tak skomentowanego kodu, przedstawiającego interfejs
klasy Tournament systemu ARENA. Komentarz nagłówkowy zawiera opis przeznaczenia klasy,
jej autorów, bieżącą wersję i odniesienie do powiązanych klas. Znaczniki @see wykorzystywa-
ne są przez Javadoc do generowania wykazów odwołań skrośnych (cross reference) między
klasami. Po komentarzu nagłówkowym następują deklaracje klas i metod. Stosowny dla
każdej metody komentarz zawiera jej krótki opis i przeznaczenie oraz specyfikację parame-
trów i typu zwracanego wyniku. Gdy z daną metodą związane są ograniczenia, są one spe-
cyfikowane w tym komentarzu za pomocą znaczników @pre, @post i (^invariant. Pierwsze
zdanie każdego komentarza i wszystkie zawarte w tym komentarzu znaczniki ekstrahowane
są i formatowane przez Javadoc.

Listing 9.2. Znacznikowane komentarze Javadoc stowarzyszone z kodem źródłowym klasy Tournament

/** Turniej, reprezentowany przez obiekt klasy Tournament, jest serię meczów,
* reprezentowanych przez instancje klasy Match, rozgrywanych między graczami,
430 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

* reprezentowanymi przez instancje klasy Player. Mecz kończy się wyłonieniem


* dokładnie jednego zwycięzcy. Gra (reprezentowana przez specjalizację klasy
* Game) i styl rozgrywania turnieju (reprezentowany przez specjalizację klasy
* TournamentStyle) scj specyficzne dla ligi organizującej turnie) (reprezentowanej
* przez instancję klasy League).

* Każdy turniej jest początkowo pusty; po jego ogłoszeniu dołączają do niego gracze,
* planowane są mecze, które następnie zostają rozgrywane.

* Niezmienniki:
*

* Maksymalna liczba graczy biorących udział w turnieju jest liczbą dodatnią:


* @invariant getMaxNumPlayers >0

* Aktualna liczba graczy zarejestrowanych w turnieju nie przekracza nigdy ich


* maksymalnej dopuszczalnej liczby:
* @invariant getPlayersQ.sizeQ < getMaxNumPlayers()
*

* Każdy turniej skojarzony jest z konkretną ligą i posiada niepustą nazwę:


* @invariant getLeagueQ != null and getNameQ .'= null
V

p u b l i c c l a s s Tournament {

... definicje pól...

/** Publiczny konstruktor. Nowy turniej powstaje w kontekście określonej


* ligi (league), posiada określoną nazwę i definiuje maksymalną dopuszczalną
* liczbę graczy (maxNumPlayers). Wartości tych atrybutów nie mogą być później
* zmieniane.
7
p u b l i c Tournament (League league, String name, i nt maxNumPlayers) {...}

/** Zwrócenie liczby graczy aktualnie zarejestrowanych w turnieju


7
p u b l i c List getPlayers() { ... }

/** Rejestrowanie nowego gracza w turnieju


*

* @pre HsPlayerAccepted(p)
* @pre getPlayersQ.sizeQ < getMaxNumPlayersQ
* @post isPlayerAccepted(p)
* @post getPlayersQ.sizeQ = self@pre.getPlayers().size() +1
*/
public void acceptPlayer (Player p) ( ... }

/** Wycofywanie gracza z turnieju. Zakłada się, że gracz ten jest


* zarejestrowany w turnieju
9.5. Zarządzanie projektowaniem obiektów 431

* @pre isPlayerAccepted(p)
* @post HsPlayerAccepted(p)
* @postgetPlayers().size() = self@pre.getPlayers().size() -1
V
public void removePlayer(Player p) { ... }

Utrzymywanie materiału źródłowego dla dokumentu ODD w postaci integralnej z ko-


dem źródłowym aplikacji ułatwia programistom zapewnienie aktualności wspomnianego do-
kumentu. Dla typowego systemu o niebanalnym zastosowaniu treść dokumentu ODD może
przekładać się na setki czy nawet tysiące stron dokumentacji. Co więcej, treść ta ewoluuje bar-
dzo szybko w czasie projektowania obiektów i integrowania podsystemów, w miarę jak pro-
gramiści coraz lepiej rozumieją potrzeby poszczególnych podsystemów oraz znajdują usterki
w ich specyfikacjach. Z tego względu wszystkie wersje dokumentu ODD powinny być dostępne
w formie elektronicznej, na przykład w postaci zbioru stron WWW, a poszczególne kompo-
nenty dokumentu ODD powinny być poddane zarządzaniu konfiguracją i synchronizowane
z odpowiadającymi im fragmentami (plikami) kodu źródłowego. (Zarządzanie konfiguracją
opiszemy dokładniej w rozdziale 13. „Zarządzanie konfiguracją").
W tym miejscu warto zwrócić uwagę na fakt, że automatyczne generowanie dokumentu
ODD nie może być substytutem projektowania obiektów. Chociaż doświadczeni programiści
zdolni są do przechowywania całości projektu obiektów w głowie i dokumentowania tego
projektu wyłącznie w formie komentarzy Javadoc, nie jest to praktyka zalecana dla ogółu.
Aktywności związane z projektowaniem obiektów i specyfikowaniem interfejsów powinny
poprzedzać kodowanie i skupiać się na definiowaniu interfejsów klas. Gdy definicje interfej-
sów okażą się stabilne, programiści powinni przystąpić do generowania namiastek klas (na
przykład za pomocą narzędzi typu CASE) i wypełniania tych namiastek stosownym kodem
stanowiącym implementację interfejsów. Jako że dokumentacja zintegrowana będzie z kodem
źródłowym, łatwo będzie ją modyfikować w związku z wprowadzaniem nieuchronnych zmian
do tego kodu. Przystąpienie do pisania dokumentacji dopiero po ukończeniu kodowania spo-
woduje, że dokumentacja ta będzie najprawdopodobniej wysoce niekompletna, a sam projekt
stanie się bardzo skomplikowany.

9.5.2. Przydzielanie odpowiedzialności


Faza projektowania obiektów angażuje ogromną liczbę uczestników wykorzystujących i mo-
dyfikujących olbrzymie porcje informacji. W celu zapewnienia, iż zmiany w poszczególnych
interfejsach będą należycie dokumentowane i komunikowane, konieczne jest przypisanie po-
szczególnym uczestnikom ról związanych z kontrolowaniem, komunikowaniem i imple-
mentowaniem tych zmian. Szczegółowo role takie powinny zostać przypisane członkom ze-
społu architektonicznego odpowiedzialnym za projekt systemu i interfejsy podsystemów,
łącznikom odpowiedzialnym za komunikację między zespołami i menedżerom konfiguracji,
którzy odpowiadają za śledzenie wspomnianych zmian.
Poniżej przedstawiamy przykłady wspomnianych ról; podobnie jak w przypadku innych
aktywności, także i tym razem danemu uczestnikowi można przypisać kilka ról.
432 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

« Główny architekt odpowiedzialny jest za opracowanie wytycznych i konwencji


związanych z kodowaniem jeszcze przed rozpoczęciem projektowania obiektów. Jak
w przypadku wszystkich konwencji, krytycznym czynnikiem jest nie tyle konkretna
ich postać, ile konsekwentne stosowanie się do nich. Główny architekt odpowiedzialny
jest także za zapewnienie spójności z uprzednio podjętymi decyzjami udokumento-
wanymi w formie SDD i ODD.
• Łącznicy architektoniczni dokumentują publiczne interfejsy podsystemów, za które
są odpowiedzialni. Prowadzi to do wstępnego szkicu dokumentu ODD, wykorzysty-
wanego przez programistów. Łącznicy architektoniczni odpowiedzialni są także za
negocjowanie zmian w publicznych interfejsach. Sukces takich negocjacji jest kwestią
nie tyle konsensusu, co komunikacji: programiści uzależnieni od danego interfejsu
zgodzą się na wszelkie jego zmiany, jeśli zostaną o nich odpowiednio wcześnie uprze-
dzeni. Łącznicy architektoniczni wraz z głównym architektem tworzą zespół ar-
chitektoniczny.
• Projektanci obiektów doskonalą i uszczegółowiają specyfikacje interfejsu klasy lub
podsystemu, za implementację których są odpowiedzialni.
• Menedżer konfiguracji uskutecznia wprowadzanie zmian do interfejsów i dokumentu
ODD, gdy tylko postać tych zmian zostanie sprecyzowana. Menedżer konfiguracji
czuwa także nad zgodnością kodu źródłowego z poszczególnymi rewizjami doku-
mentu ODD.
• Dokumentalista techniczny, jako członek zespołu dokumentacyjnego, nadaje osta-
teczną formę dokumentowi ODD, dbając przy tym o jego spójność pod względem
struktury i treści. Weryfikuje także treść tego dokumentu z przyjętymi wytycznymi.

Podobnie jak w fazie projektowania systemu, zespół architektoniczny stanowi mecha-


nizm integrujący — pilnuje zgodności dokonywanych zmian z celami projektowymi. Zespół
architektoniczny wraz z dokumentalistami technicznymi dba natomiast o zgodność zmian
z przyjętymi konwencjami i wytycznymi.

9.5.3. Wykorzystywanie kontraktów w analizie wymagań


Niektóre podejścia do procesu analizowania wymagań zalecają używanie mechanizmu ogra-
niczeń znacznie wcześniej, na przykład już podczas definiowania obiektów encji. Formalnie
rzecz biorąc, język OCL może być tak samo przydatny na etapie analizy wymagań jak na etapie
projektowania obiektów. Wszystko zależy od projektantów decydujących o wyborze konkret-
nej metodologii oraz określonego poziomu formalizacji przy identyfikowaniu i dokumento-
waniu operacji. Poniżej przedstawiamy kilka kryteriów decydujących o konkretnym kształcie
wspomnianego wyboru.

• Komunikacja między uczestnikami. W procesie tworzenia oprogramowania głównym


narzędziem komunikowania się jego uczestników są różnorodne modele. Dla róż-
nych typów uczestników odmienne typy modeli są bardziej odpowiednie od innych:
z punktu widzenia klienta przypadki użycia i makiety interfejsów użytkownika są
z reguły bardziej czytelne i intuicyjne niż ograniczenia OCL, te ostatnie jednak sta-
nowią lepszą (bo bardziej precyzyjną) specyfikację dla użytkownika klasy.
10.6. Analiza przypadku — system ARENA 433

• Poziom szczegółowości i dynamika zmian. Zastosowanie formalnego mechanizmu


ograniczeń do modelu analitycznego zmusza do rozumienia samych wymagań znacz-
nie głębszego niż w przypadku ich opisu werbalnego. Gdy tylko dostępna jest odpo-
wiednio obszerna i precyzyjna informacja — pochodząca od klienta lub użytkownika
czy wynikająca z ogólnego rozumienia dziedziny aplikacyjnej — model analityczny
staje się bardziej kompletny. Gdy tej informacji brakuje, klient i programiści zmuszeni
zostają do wczesnego podejmowania decyzji, dla których jeszcze nie istnieją wystar-
czająco klarowne przesłanki. Konsekwencją tego będzie najprawdopodobniej zwięk-
szona dynamika zmian (i wynikający z niej zwiększony koszt) w późniejszych fazach
rozwoju systemu.
• Poziom szczegółowości zbieranych wymagań. Podobnie przedwczesne żądanie od
użytkowników zbyt szczegółowych informacji może wymagać wysiłku większego niż
później, gdy dostępne będą wczesne wersje interfejsów użytkownika i będzie można
zademonstrować w praktyce wybrane mechanizmy. Rezygnacja ze zbytniej szczegó-
łowości na początku zbierania wymagań jest jednak dopuszczalna przy założeniu, że
przyszłe modyfikacje poszczególnych komponentów nie będą zabiegami kosztownymi
i będą dobrze zlokalizowane (modyfikacja danego komponentu będzie mieć nikły
wpływ na resztę systemu). Przykładem tytułowych szczegółów mogą być szczegóły
kompozycji interfejsu użytkownika lub szczegóły scenariuszy poszczególnych
dialogów.
• Wymogi dotyczące testowania. Testowanie to konfrontacja specyfikowanego zacho-
wania systemu lub klasy z jej faktycznym zachowaniem. W testach zautomatyzowa-
nych lub przy restrykcyjnych wymogach testowych (spotykanych w dziedzinach
aplikacyjnych o krytycznym znaczeniu: kierowaniu ruchem, medycynie, farmacji
i tym podobnych) zestawy testowe muszą być zaprojektowane szczególnie precyzyjnie.
Wielce pomocny w tym dziele może okazać się formalny mechanizm ograniczeń
— pod warunkiem oczywiście, że będą one właściwie sformułowane. (Testowaniem
zajmiemy się szczegółowo w rozdziale 11. „Testowanie").

9.6. Analiza przypadku — system ARENA


Tak jak w poprzednich rozdziałach, wykorzystamy system ARENA do obszerniejszego zilustrowa-
nia opisywanych w rozdziale metod i koncepcji. Skoncentrujemy się na klasie TournamentStyle
i klasach z nią powiązanych. Specjalizacje klasy TournamentStyle odpowiedzialne są za two-
rzenie zbiorów meczów (Match), przydzielanie graczy (PI ayer) do turniejów (Tournament)
oraz moment rozpoczynania i kończenia każdego z meczów. Specyfikacja kontraktu dla tych
klas ma znaczenie o tyle krytyczne, że abstrakcyjna klasa TournamentStyle stanowi dla nieza-
leżnych programistów otwarty interfejs do definiowania nowych stylów rozgrywania turnie-
jów; granice między klasą TournamentStyle a resztą systemu powinny być wyraźnie sprecyzo-
wane, aby owo definiowanie nowych stylów mogło odbywać się bez konieczności zrozumienia
kodu źródłowego systemu ARENA.
Rozpoczniemy od zdefiniowania brakujących operacji i klas, następnie sformułujemy
kontrakty dla abstrakcyjnych klas powiązanych z klasą TournamentStyle. Na koniec poka-
żemy, na przykładzie definiowania subklas tej klasy (między innymi klasy KnockOutStyl e),
jak w związku z tym kontrakty te mogą stawać się bardziej rygorystyczne.
434 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

9.6.1. Identyfikowanie brakujących operacji


w klasach TournamentStyle i Round
Z modelu analitycznego systemu ARENA wiemy już, że klasa TournamentStyle odpowiedzialna
jest za odwzorowywanie graczy (Player) zarejestrowanych w danym turnieju (Tournament)
w serię meczów (Match), zgodnie ze stylem rozgrywek reprezentowanym przez odpowiednią
subklasę. Przykładem takiej subklasy może być KnockOutStyl e reprezentująca styl „turniejowy",
zgodnie z którym tylko zwycięzcy meczów mają prawo dalszego uczestnictwa w turnieju. Inna
subldasa — RoundRobi nStyl e — reprezentuje styl „karuzelowy", zgodnie z którym każdy
gracz rozgrywa dokładnie jeden mecz z każdym ze swych konkurentów (niezależnie od wyni-
ków poszczególnych meczów).
Operacje klasy TournamentStyle wywoływane są przez obiekty klasy Tournament w celu
generowania serii meczów, skupimy się więc na interfejsie API klasy TournamentStyle. Za-
uważmy więc najpierw, że wśród meczów generowanych we wspomnianej serii istnieje relacja
częściowego porządku ze względu na czas ich rozgrywania: niektóre mogą być rozgrywane
równocześnie (na przykład wszystkie mecze pierwszej rundy mistrzostw), inne natomiast
mogą rozpocząć się po zakończeniu wybranych (na przykład mecz finałowy może rozpocząć
się dopiero po rozstrzygnięciu obu pojedynków w półfinale). Zauważmy ponadto, że nie dla
wszystkich meczów wspomnianej serii można a priori zaplanować obsadę: w stylu „tur-
niejowym" (KnockOutStyle) tylko zwycięzcy n-tej rundy mają prawo rozgrywania me-
czów w rundzie n+1, obsada meczów rundy n+1 znana jest więc dopiero po zakończeniu
rundy n. Okoliczności te prowadzą do wymagań, dotyczących:

• reprezentowania harmonogramu meczów: reprezentacja grafiku meczów rozgry-


wanych w ramach turnieju powinna mieć postać możliwą do wykorzystania w do-
wolnej subklasie klasy TournamentStyle,
• przyrostowego planowania: metody klasy TournamentStyle powinny umożliwiać
planowanie meczów przyrostowo, w miarę postępów w przebiegu turnieju,
» niezależności od gier: grafik meczów powinien być niezależny od konkretnej gry;
szczególnie Idasa Match stanowiąca część fabryki abstrakcyjnej Game powinna być
wolna od powyższych ograniczeń.

Aby uczynić zadość tym wymaganiom, zdefiniujemy nową klasę Round (patrz rysunek
9.13). Klasa ta reprezentuje pojedynczą rundę, czyli zbiór meczów, które mogą być rozgrywane
równolegle, niezależnie od siebie, zatem harmonogram turnieju może być reprezentowany
w postaci listy (sekwencji) rund (obiektów subklasy Round). W związku z tym, dodamy do
klasy Tournament operację pl anRounds (), zwracającą w wyniku wspomnianą listę, powstałą
w wyniku utworzenia obiektów reprezentujących wszystkie mecze i zorganizowania ich w serię
rund. Operacja pl anRounds () odpowiedzialna będzie ponadto za przydzielenie graczy do
wszystkich meczów pierwszej rundy (i ewentualnie do następnych rund, jeżeli taki przydział
a priori jest dla tych rund możliwy). Ewentualny przydział graczy do kolejnych (nieobsadzonych
jeszcze) rund realizowany będzie za pomocą operacji Round.plan (), wywoływanej na rzecz
obiektów reprezentujących te rundy.
10.6. Analiza przypadku — system ARENA 435

Rysunek 9.13. Nowa klasa Round oraz jej skojarzenia z klasami Tournament i Match. Zmiany wyróż-
nione są pogrubionymi liniami

W celu umożliwienia ekstenderom definiowania nowych stylów rozgrywek klasy


TournamentStyle i Round są Masami abstrakcyjnymi, podlegającymi specjalizowaniu dla
konkretnego stylu — przykładowo dla stylu „turniejowego" specjalizacje te mają postać klas
KnockOutStyl e i KnockOutRound. Owe specjalizacje nie są bezpośrednio widoczne z poziomu
klasy Tournament, korzysta ona bowiem jedynie z interfejsu zdefiniowanego przez wymienio-
ne klasy abstrakcyjne i niezależna jest od konkretnego stylu wybieranego przez obiekt klasy
League.

9.6.2. Specyfikowanie kontraktów dla klas TournamentStyle i Round


Zadaniem klasy TournamentStyle jest tworzenie listy rund (Round) dla danego turnieju
(Tournament) — lista ta zarządzana jest przez obiekt reprezentujący ten turniej. Klasa
TournamentStyle nie przechowuje więc żadnych informacji o stanie, zatem nie są z nią
związane żadne niezmienniki.
Operacja TournamentStyle.planRounds () posiada jeden parametr, reprezentujący
turniej, dla którego odbywa się planowanie meczów. Podstawowym warunkiem wstępnym tej
operacji jest niedopuszczalność ponownego planowania meczów w danym turnieju — z tur-
niejem reprezentowanym przez wspomniany parametr nie może być więc już skojarzona
lista rund.
436 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

Dla konkretnych subklas klasy TournamentStyle poszczególne warunki wstępne mogą


dotyczyć dopuszczalnej liczby graczy. Przykładowo dla stylu „turniejowego" wymaga się, by
liczba ta była potęgą liczby 2, wtedy bowiem liczba zawodników uczestnicząca w każdej z rund
będzie zawsze parzysta i da się sformować pary na poszczególne mecze. Jako że dopuszczalna
liczba graczy wynika ze specyfiki danego stylu i może być różnie definiowana dla odmiennych
subklas, dodaliśmy do klasy TournamentStyle operację legalNumPlayers () weryfikującą pod
tym kątem poprawność liczby przekazanej jako parametr. Zauważmy, że określenie dopusz-
czalnej liczby graczy dla konkretnego stylu nie może pozostawać w sprzeczności z maksymalną
liczbą graczy zdefiniowaną globalnie dla turnieju (Tournament .maxNumPl ayers).

/* Planowanie meczów może być wykonane tylko dla turnieju,


* któremu nie przyporządkowano jeszcze listy rund
7
context TournamentStyle::planRounds(t:Tournament) pre:
t <> null and t.rounds = null and legalNumPlayers(t.players->size)

Alternatywnym podejściem byłoby zdefiniowanie dopuszczalnej liczby graczy jako warunku


wstępnego dla operacji pl anRounds () w poszczególnych subklasach. Wtedy jednak, mając na
względzie zasadę zastępowania Liskov, musielibyśmy zdefiniować analogiczny warunek dla
tej operacji w samej klasie TournamentStyle — i to warunek nie mniej restrykcyjny niż w do-
wolnej z subklas (patrz sekcja 9.4.5). W przeciwnym razie programista implementujący kon-
kretny styl mógłby łatwo doprowadzić do załamania systemu ARENA — przy zachowaniu
wszystkich kontraktów! Jako że system ma być z definicji niezależny od konkretnego stylu,
trudno w ogóle taki ogólny warunek zdefiniować inaczej niż w formie ogólnej metody abs-
trakcyjnej — co też uczyniliśmy.
Spośród warunków końcowych narzuconych na operację TournamentStyle.planRounds ()
pierwszy wyraża konieczność przyporządkowania każdego gracza do co najmniej jednego
meczu. Drugi warunek określa rozłączność czasową poszczególnych rund; rozłączność ta
wymagana jest z tego względu, że dany gracz może zostać przyporządkowany do więcej
niż jednej rundy. Aby ułatwić sobie jego sformułowanie, dodamy do klasy Round operacje
getStartDate() i getEndDate () zwracające (odpowiednio) czas rozpoczęcia najwcześniejszego
meczu rundy i czas zakończenia meczu najpóźniejszego. Dwie dowolne rundy są rozłączne
czasowo, rozpoczęcie drugiej z nich następuje później niż zakończenie pierwszej.

/* Każdy z graczy musi być przyporządkowany


* do co najmniej jednego meczu
7
context TournamentStyle::planRounds(t:Tournament) post:
t.getPlayers()->forAll(p|p.getMatches(tournament)->notEmpty()

/* Dowolne dwa mecze należące do różnych rund muszą być rozłączne czasowo 7
context TournamentStyle::planRounds(t:Tournament) post:
result->forAll(rl,r2| rl<>r2 implies
rl.getEndDate().before(r2.getStartDate())
or
rl.getStartDate().after(r2.getEndDate())
10.6. Analiza przypadku — system ARENA 437

Kolejny warunek dotyczy wykluczenia możliwości przyporządkowania tego samego


gracza do więcej niż jednego meczu w danej rundzie — co jest fizycznie możliwe, lecz sprzeczne
z dziedziną aplikacyjną, jako że mecze danej rundy rozgrywane są równolegle. Aby uprościć
jego sformułowanie, dodamy do klasy Pl ayer metodę getMatches () z jednym parametrem
reprezentującym turniej: wynikiem zwracanym przez tę metodę jest lista meczów danego tur-
nieju, do których gracz jest przyporządkowany. Dowolne dwa różne mecze z tej listy muszą
należeć do odmiennych rund. Warunek ten, mający postać niezmiennika, jest jednocześnie
kontraktem dla klasy Round:

/* Żaden z graczy nie może być przyporządkowany


* do dwóch różnych meczów tej samej rundy
*/
context Round inv:

matches->forAl1(ml:Match|
ml.players->forAll(p:Player|
p.matches->forAll(m2:Match| ml <> m2 implies ml.round <> m2.round)))

Zauważmy, że niezmiennik ten pozostaje prawdziwy nawet wówczas, gdy żaden gracz
nie jest przyporządkowany do meczu 8 .
Przyjrzyjmy się teraz informacji o stanie, przechowywanej w ramach klasy Round. Obiekt
tej klasy może znajdować się w jednym z trzech stanów, reprezentujących (odpowiednio) rundę:

• niezaplanowaną — to stan początkowy, w którym istnieje lista meczów, lecz nie dla
wszystkich zdefiniowano obsadę,
• zaplanowaną — w tym stanie do wszystkich meczów przyporządkowani są gracze;
obiekt wchodzi w ten stan po wykonaniu operacji pl an (),
• zakończoną — ten stan oznacza zakończenie rundy: wszystkie mecze zostały rozegra-
ne, w każdym wyłoniony został zwycięzca.

Informacja o stanie obiektu udostępniana będzie przez jego dwie metody: Round, is
•^Planned() i Round. isCompleted(), zwracające wartość True dla stanu reprezentującego rundę
(odpowiednio) „zaplanowaną" i „zakończoną". Informacja ta jest niezbędna zarówno dla
klasy Round, jak i dla klasy Tournament. Przydzielanie graczy do meczów danej rundy mo-
że wymagać zakończenia rundy poprzedniej, podobnie przed zakończeniem bieżącej rundy
nie może być rozpoczęta runda następna; za spełnienie pierwszego z tych warunków odpowie-
dzialna jest klasa Round, za spełnienie drugiego — klasa Tournament. Zapis odpowiednich
ograniczeń widoczny jest na listingu 9.3.

Listing 9.3. Kontrakt dla klasy Round

/* Wywołanie operacji plan() dla danej rundy po zakończeniu


* wszystkich poprzednich rund wprowadza tę rundę w stan "zaplanowana"
7

8
Dla pustej kolekcji kwantyfikator forAl 1 () zawsze zwraca wartość true, niezależnie od podanego
warunku — przyp. tłum.
438 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

context R o u n d . p l a n ( ) post:
@pre.getPreviousRound() .isCompletedO implies isPlannedQ
/* Runda znajduje się w stanie "zaplanowana", jeśli dla wszystkich
* jej meczów zdefiniowana została obsada
V
context R o u n d . i s P l a n n e d ( ) post:
r e s u l t implies
matches->forAl1(m|m.players->size = tournament.league.game.numPlayersPerMatch)
I* Runda znajduje się w stanie "zakończona", jeśli zakończone zostały
* wszystkie jej mecze, czyli w każdym meczu wyłoniony został zwycięzca
7
context R o u n d . i s C o m p l e t e d O post:
r e s u l t implies matches->forAll(m[ m.winner <> null)

Jak za chwilę zobaczymy, informacja niezbędna do planowania rundy może być do-
stępna dopiero po zakończeniu poprzedniej rundy (choć nie jest to regułą).

9.6.3. Specyfikowanie kontraktów dla klas KnockOutStyl e i KnockOutRound


Subklasy dziedziczą kontrakty po swych superklasach (patrz sekcja 9.4.5), tak więc klasy
KnockOutStyl e i KnockOutRound dziedziczą swe kontrakty po klasach (odpowiednio)
TournamentStyle i Round. Subklasy mają przy tym prawo zaostrzać odziedziczone warunki
końcowe i niezmienniki i (lub) łagodzić odziedziczone warunki wstępne. Umożliwia to precy-
zyjne doskonalenie klas i szczegółowe dokumentowanie różnic tego doskonalenia w stosunku
do innych subklas (na przykład klasy RoundRobi nStyl e).
Rozpoczniemy od określenia sposobu weryfikowania podanej liczby graczy przez metodę
KnockOutStyl e. 1 egal NumPl ayers (). Dopuszcza się liczbę graczy będącą całkowitą potęgą
liczby 2 (1, 2,4, 8 ...) nieprzekraczającą maksymalnej liczby graczy zdefiniowanej dla turnieju.
Weryfikację tę przeprowadzimy w sposób rekurencyjny, definiując przy okazji liczbę meczów
dla każdej rundy. Runda finałowa obejmuje — oczywiście — jeden mecz, natomiast liczba
meczów w poprzedniej rundzie jest dwukrotnie większa niż w rundzie następnej. Ponadto
gracz ma prawo uczestniczenia w danej rundzie, gdy jest ona pierwsza rundą albo gracz jest
jednym ze zwycięzców rundy poprzedniej. Ze względu na rekurencyjną zależność parametrów
poszczególnych rund konieczny jest również warunek, że planowanie bieżącej rundy wymaga
uprzedniego zakończenia rundy poprzedniej. Zmodyfikowany w związku z tym kontrakt dla
klasy Round widoczny jest na listingu 9.4.

Listing 9.4. Udoskonalony kontrakt dla klasy Round

I* Liczba graczy musi być potęgę całkowitę liczby 2 V


context K n o c k O u t S t y l e : : l e g a l N u m P l a y e r s ( n : i n t ) post:
result = (floor(log(n)/log(2)) = (log(n)/log(2)))9

9
Wykorzystuje się tu własność, że jeśli x jest potęgą liczby 2, to floor(log, x)= log, x (floor oznacza część
całkowitą wyrażenia). Wartość logarytmu przy podstawie 2 oblicza się na podstawie tożsamości
log, x = (ln oznacza logarytm naturalny, reprezentowany przez funkcję 1 og ()) —przyp. tłum.
In 2
10.6. Analiza przypadku — system ARENA 439

/* Runda finałowa obejmuje jeden mecz, liczba meczów w rundzie


* poprzedniej jest dwukrotnie większa niż w rundzie następnej
7
context K n o c k O u t S t y l e : : p l a n R o u n d s ( t : T o u r n a m e n t ) post:
result->forAll(index:Integer|
if (index = result->size) then
result->at(index) .matches->size = 1
el se
result->at(index).matches->size =
(2*result->at(index+l).matches->size))
endif)

/* W pierwszej rundzie uczestniczę wszyscy gracze, w każdej


* z następnych rund uczestniczę tylko zwycięzcy z poprzedniej
* rundy
7
context KnockOutRound inv:
previousRound = null or matches.players->forAl1(p|
round.previousRound.matches->exists(m| m.winner = p))

/* Nie można rozpoczęć planowania bieżęcej rundy, jeśli


* nie zakończyła się runda poprzednia
7
context K n o c k O u t R o u n d : : p l a n ( ) post:
not self@pre.getPreviousRound() .isCompietedO implies not isP1anned()

9.6.4. Wnioski
Definiując kontrakty dla klas TournamentStyle, Round, KnockOutStyle i KnockOutRound,
mieliśmy okazję przekonać się, że:

• identyfikowanie brakujących operacji i definiowanie kontraktów to dwie przeplatające


się aktywności. Definiując kontrakt dla danej klasy, przyglądamy się uważnie funk-
cjonowaniu jej obiektów i zauważamy nowe aspekty ich zachowania i warunki
graniczne. W przytoczonym przykładzie tworzyliśmy nową klasę Round, modelującą
związki między klasami Match i Tournament. Mimo iż związki te odnoszą się raczej
do dziedziny aplikacyjnej, ich uwzględnienie doprowadziło do powstania nowego
obiektu realizacyjnego.
• definiowanie kontraktów ujawnia potrzebę dodawania nowych operacji pomocni-
czych. Mimo iż bieżący stan obiektu może być zidentyfikowany na podstawie wartości
jego atrybutów i powiązań z innymi obiektami, wygodniej powierzyć tę identyfikację
oddzielnym metodom pomocniczym. Dzięki temu upraszcza się znacznie postać ogra-
niczeń zawartych w kontrakcie klasy, a sama klasa staje się łatwiejsza w testowaniu
(testowaniem zajmiemy się w rozdziale 11. „Testowanie").
• definiowanie kontraktów prowadzi do łepszych abstrakcji. W ramach dziedziczenia
kontraktów jedynie warunki wstępne mogą być osłabiane, warunki końcowe i nie-
zmienniki muszą zostać zachowane i być może zaostrzone. W związku z tym, tworząc
440 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

kontrakt dla klasy abstrakcyjnej, musimy klasę tę wyposażyć w metody odzwier-


ciedlające własności obiektów będących instancjami jej subklas. I tale właśnie za pomocą
metody TournamentStyle. legalNumPlayers () udało nam się rozwiązać problem
polegający na tym, że dla konkretnego stylu rozgrywek dopuszczalna liczba graczy
może być określona przez dowolnie skomplikowaną formułę. Wyposażanie klas abs-
trakcyjnych w metody tego rodzaju skutkuje lepiej zdefiniowanym metamodelem
dla konkretnych subklas.

Definiując kontrakty, zmuszeni jesteśmy do precyzowania dopuszczalnych wartości dla


poszczególnych parametrów. Kolejnym krokiem staje się zdefiniowanie zachowania poszcze-
gólnych operacji, w przypadku gdy użytkownik klasy nie przestrzega zdefiniowanego dla tej
klasy kontraktu. Użyteczny staje się wówczas mechanizm wyjątków, który opiszemy szcze-
gółowo w następnym rozdziale.

9.7. Literatura uzupełniająca


Historycznie pierwszymi językami łączącymi ograniczenia z orientacją obiektową były ThingLab,
opisany przez A. Borninga [Borning, 1981], Kaleidoscope, omówiony w pracy B. Freemana-
-Bensona [Freeman-Benson, 1990], i Eiffel, który w swojej książce opisał B. Meyer [Meyer,
1997]. Termin „projektowanie za pomocą kontraktów" (design by contracts) sformułowany
został przez B. Meyera, który w książce [Meyer, 1997] dostarcza szczegółowych wskazówek
metodologicznych dotyczących pisania kontraktów, wraz z konkretnymi przykładami (w języ-
ku Eiffel) ich definiowania oraz postępowania w przypadkach naruszenia. Formułowanie
ograniczeń i interfejsów w kategoriach abstrakcyjnych typów danych w języku CLU przed-
stawione jest w książce B. Liskov i J. Guttaga [Liskov i Guttag, 1986]. Koncepcja asercji poja-
wiła się na kanwie prac nad poprawnością oprogramowania, prowadzonych i opisywanych przez
R. W. Floyda [Floyd, 1967], C. A. R. Hoarego [Hoare, 1969] i E. W. Dijkstrę [Dijkstra, 1976],
W języku Java projektowanie za pomocą kontraktów pojawiło się w postaci narzędzia
o nazwie iContract, opisanego przez R. Kramera [Kramer, 1998], weryfikującego — w czasie
wykonywania programu — spełnienie ograniczeń zapisanych w postaci znacznikowanych
komentarzy Javadoc. Ostatnio w kręgach społeczności Java koncepcja „projektowania przez
kontrakty" znajduje odzwierciedlenie w postaci rozmaitych projektów kategorii open source,
między innymi Contract4J [Contract4J] i jContractor [jContractor],

9.8. Ćwiczenia
9.1. Dla interfejsu List z pakietu j a v a . u t i l , reprezentującego uporządkowaną ko-
lekcję obiektów, zdefiniuj warunki wstępne i warunki końcowe w kontekście na-
stępujących operacji:
• i n t s i z e ( ) zwracającej liczbę elementów w liście,
• voi d add (Object e) powodującej dołączenie obiektu e na końcu listy,
• void remove(0bject e) powodującej usunięcie obiektu e z listy,
® boolean contains(Object e) badającej, czy obiekt e znajduje się w liście,
® Object get ( i n t idx) zwracającej obiekt identyfikowany przez indeks i dx
(pierwszy obiekt w liście ma indeks równy 0).
8.8. Ćwiczenia 441

9.2. Sformułuj w języku OCL warunki wstępne i warunki końcowe dla następujących
operacji interfejsu Set z pakietu Java, uti 1:
• i n t s i ze () zwracającej liczbę elementów w zbiorze,
• void add (Object e) powodującej dodanie obiektu e do zbioru; jeżeli obiektu e
znajduje się już w zbiorze, operacja nie powoduje żadnej akcji,
• void remove(0bject e) powodującej usunięcie obiektu e ze zbioru,
• boolean contains(Object e) badającej, czy obiekt e znajduje się w zbiorze.
9.3. Dla klasy Collection z pakietu j a v a . u t i l , będącej superklasą dla klas List i Set,
sformułuj warunki wstępne i warunki końcowe dla wymienionych poniżej operacji:
• i n t s i ze () zwracającej liczbę elementów w kolekcji,
• void add(0bject e) powodującej dodanie obiektu e do kolekcji,
• void remove(Object e) powodującej usunięcie obiektu e z kolekcji,
• boolean contains(Object e) badającej, czy obiekt e znajduje się w kolekcji.
Następnie zmodyfikuj ograniczenia stworzone w ramach ćwiczeń 9.1 i 9.2 tak, by
spełnione były zasady dziedziczenia kontraktów. Nie zapomnij o zasadzie zastę-
powania Liskov.
9.4. Rozpatrzmy klasy Rectangl e i Square reprezentujące (odpowiednio) prostokąt i kwa-
drat; Square jest subklasą klasy Rectangl e.
• Sformułuj warunki końcowe dla operacji R e c t a n g l e . s e t M i d t h ( w : i n t )
i Rectangl e. setHei ght (h: i nt) w kategoriach operacji Rectangl e. getWi dth ( ) : i nt
i Rectangle.getHeight() : i n t .
o Sformułuj dla klasy Square niezmiennik stanowiący, że wysokość i szerokość
kwadratu zawsze są równe.
• Czy w kontekście operacji Square.setWidthQ i Square.setHeightQ speł-
nione są reguły dziedziczenia kontraktów opisane w sekcji 9.4.5? Jeśli nie, dla-
czego? Co trzeba zmienić w modelu, by zapewnić ich spełnienie?
9.5. Sformułuj w języku OCL niezmiennik wyrażający fakt posortowania elementów listy.
9.6. Dla posortowanego drzewa binarnego sformułuj w języku OCL niezmienniki stano-
wiące, że:
• dla każdego węzła jego lewe poddrzewo albo jest puste, albo zawiera w swych wę-
złach liczby całkowite nie większe od liczby całkowitej zawartej w tymże węźle,
• dla każdego węzła jego prawe poddrzewo albo jest puste, albo zawiera w swych wę-
złach liczby całkowite nie mniejsze od liczby całkowitej zawartej w tymże węźle,
• drzewo jest zrównoważone 10 .
9.7. Wyobraź sobie zwykłe skrzyżowanie dwóch prostopadłych dróg dwukierunkowych,
z czterema sygnalizatorami świetlnymi. Załóżmy prosty algorytm cyklicznego

10
Drzewo binarne jest zrównoważone, jeśli dla każdego węzła wysokości jego poddrzew są jednakowe.
Wysokością drzewa nazywamy liczbę węzłów na jego najdłuższej ścieżce prowadzącej od korzenia do li-
ścia; wysokość drzewa pustego równa jest umownie 0 — przyp. tłum.
442 Rozdział 9. • Projektowanie obiektów: specyfikowanie interfejsów

przełączania świateł tak, że dopuszczenie ruchu w jednym kierunku wiąże się


z równoczesnym zablokowaniem ruchu w kierunku prostopadłym. Przedstaw model
każdego z czterech sygnalizatorów jako obiektu klasy TrafficLight, mogącego znaj-
dować się w trzech stanach: red, yel 1 ow i green, odpowiadających światłom czerwo-
nemu, żółtemu i zielonemu, przechowywanych pod postacią atrybutu s t a t e . Sfor-
mułuj w języku OCL niezmiennik wykluczający równoczesne dopuszczenie do ruchu
w obu prostopadłych kierunkach. Dodaj do modelu niezbędne skojarzenia; nie za-
pominaj, że ograniczenia OCL zapisywane są w kontekście klas, a nie obiektów.
9.8. W świetle treści sekcji 9.6.2 i 9.6.3 sformułuj ograniczenia dla klas RoundRobi nStyl e
i RoundRobinRound jako subklas TournamentStyle i Round specjalizowanych dla
„karuzelowego" (każdy z każdym) stylu rozgrywek. Zauważ, że liczba rund tur-
nieju w tym stylu zależna jest od tego, czy liczba graczy zarejestrowanych w tym
turnieju jest parzysta, czy nieparzysta i że każdy gracz uczestniczy w danej run-
dzie co najwyżej jeden raz.

Bibliografia
[Borning, 1981] A. Borning The programming language aspects of ThingLab,
a constraint-oriented simulation laboratory, w „ACM TOPLAS" 3 (4),
październik 1981.
[Contract4J] http://www.contract4j.org/
[Dijkstra, 1976] E.W. Dijkstra A Discipline of Programming, Prentice Hall, Englewood
Cliffs, NJ, 1976.
R. W. Floyd Assigning meanings to programs, w Proceedings of the
[Floyd, 1967]
American Mathematics Society Symposium in Applied Mathematics,
t. 19, str. 19 - 31, 1967.
B. Freeman-Benson Kaleidoscope: Mixing objects, constraints, and
[Freeman-Benson, 1990]
imperative programming, w „OOPSLA/SIGPLAN Notices" 25 (10):
77:88, październik 1990.
C.A.R. H o a r e An axiomatic basis for computer programming,
[Hoare, 1969]
„Communications of the ACM", t. 20, nr 6, str. 576 - 580, październik 1969.
[Horn, 1992] B. H o r n Constraint patterns as a basis for object-oriented programming,
Proceedings of the OOPSLA'92, Vancouver, Canada, 1992.
[Javadoc, 2009a] Sun Microsystems, strona główna Javadoc http://java.sun.com/j2se/javadoc/.
[Javadoc, 2009b] Sun Microsystems, How to write doc comments for Javadoc,
http://java.sun.com/j2se/javadoc/writingdoccomments/.
[j Contractor] http://jcontractor.sourceforge.net/
[Johnson i Foote, 1988] R. Johnson, B. Foote Designing reusable classes, „Journal of Object-
Oriented Programming", t. 1, nr 5, str. 22 - 35, 1988.
R. Kramer, ,,iContract-The Java Design by Contract Tool" Technology
[Kramer, 1998] of Object-Oriented Languages and Systems, IEEE Computer Society
Press, 1998, str. 295.
B. Liskov, J. Guttag Abstraction and Specification in Program Development,
[Liskov i Guttag, 1986] McGraw-Hill, New York, 1986.
Bibliografia 443

[Meyer, 1997] B. Meyer Object-Oriented Software Construction, wyd. drugie, Prentice


Hall, Upper Saddle River, NJ, 1997.
[OMG, 2006] Object Management Group, Object Constraint Language OMG Available
Specification Version 2.0, http://www.omg.org, 2006.
[Rational, 2002] Rational Corp. Rational Rose. Cupertino, CA, 2002, http://www.rational.com.
[Rumbaugh i in., 1991] J. Rumbaugh, M. Błaha, W. Premerlani, F. Eddy, W. Lorensen
Object-Oriented Modeling and Design, Prentice Hall, Englewood Cliffs,
NJ, 1991.
[Sun, 2009] Sun Microsystems, Code Conventions for the Java Programming Language.
http://www.java.sun.com/docs/codeconv/, 2009.
[TogetherSoft, 2002] TogetherSoft, Together Control Center, Raleigh, NC,
http://www.togethersoft.com, 2002.
[Warmer i Kleppe, 2003] J. Warmer, A. Kleppe The Object Constraint Language: Getting Your Modes
Ready for MDA, wyd. drugie, Addison-Wesley, Reading, MA, 2003.
10.1. Wstęp: Władca Pierścieni 446

10.2. O odwzorowywaniu ogólnie 447

10.3. Koncepcje odwzorowywania 448


10.3.1. Transformowanie modelu 449
10.3.2. Refaktoryzacja 450
10.3.3. Inżynieria postępująca 452
10.3.4. Inżynieria odwracająca 452
10.3.5. Zasady transformacji 453
10.4. Aktywności odwzorowywania 454
10.4.1. Optymalizowanie modelu obiektowego 455
10.4.2. Odwzorowywanie skojarzeń na kolekcje 458
10.4.3. Odwzorowywanie kontraktów w wyjątki 465
10.4.4. Odwzorowywanie modelu obiektowego
w schematy bazy danych 469

10.5. Zarządzanie transformacj ami 475


10.5.1. Dokumentowanie transformacji 475
10.5.2. Przydzielanie odpowiedzialności 477

10.6. Analiza przypadku — system ARENA 478


10.6.1. Statystyki systemu ARENA 478
10.6.2. Odwzorowywanie skojarzeń na kolekcje 480
10.6.3. Odwzorowywanie kontraktów w wyjątki 482
10.6.4. Odwzorowywanie modelu obiektowego
w schemat bazy danych 484
10.6.5. Wnioski 485

10.7. Literatura uzupełniająca 485

10.8. Ćwiczenia 486

Bibliografia 487
Odwzorowywanie
modelu na kod

Z rzeczami, które nigdy nie mogą się zepsuć, jest ten problem,
że gdy się zepsuję, nijak nie można ich już naprawić.
— Douglas Adams Mostly Harmless

( j d y właściwie dokonaliśmy wyboru wzorców projektowych i starannie zaprojektowali-


śmy interfejsy klas, większość problemów związanych z projektowaniem obiektów powin-
niśmy mieć już za sobą. Teoretycznie rzecz biorąc, moglibyśmy przystąpić do implementowania
systemu zaraz po fazie projektowania systemu i analizie wymagań, gdy dysponujemy jedynie
modelem analitycznym i modelem projektu systemu, jednak w fazie integrowania podsyste-
mów zapewne spotkałaby nas przykra niespodzianka. Okazałoby się bowiem, że implementa-
cje owych podsystemów w ten czy inny sposób naruszają kontrakty współpracy; odkrylibyśmy,
że na przykład nowe parametry, związane z modyfikowaniem wymagań, nie zostały udoku-
mentowane; nowe atrybuty dodane do analitycznego modelu obiektowego nie zostały uwzględ-
nione w podsystemie magazynowania danych i generalnie wiele różnych problemów rozwiąza-
nych zostało przez poszczególnych programistów w sposób odmienny i niespójny — wszystko
wskutek niedostatecznej komunikacji. Rosnąca presja na terminowe ukończenie zaplanowa-
nych zadań stanowi pokusę do rozwiązywania wielu problemów w sposób improwizowany,
a rozmaite „obejścia" wynikające z tej improwizacji przekładają się na degradację systemu.
Powstały kod w niewielkim tylko stopniu przypomina pierwotny projekt i jest niesamowicie
trudny do zrozumienia.
W tym rozdziale opiszemy kilka wybranych technik, których celem jest zapobieżenie tej
nieciekawej perspektywie; a obejmują one między innymi:

• optymalizowanie modelu klas,


• odwzorowywanie skojarzeń w kolekcje,
• odwzorowywanie niespełnienia kontraktów w wyjątki,
• odwzorowywanie modelu klas w schematy przechowywania danych.

W prezentowanych przykładach posługiwać się będziemy językiem Java, niemniej jed-


nak opisywane przez nas techniki i koncepcje dadzą się łatwo wyrazić w dowolnym języku
programowania zgodnym z orientacją obiektową.
446 Rozdział 10. • Odwzorowywanie modelu na kod

10 1 Wstęp Władca Pierścieni


Pierwsze wydanie Władcy Pierścieni J.R.R. Tolkiena ukazało się w Wielkiej Brytanii w 1954 roku;
w USA książkę wydano po raz pierwszy niespełna rok później 1 . Tolkien, profesor języka angielskiego
i uznany językoznawca, skonstruował kilka dziwacznych języków i starannie obmyślił wiele fikcyj-
nych nazw dla miejsc i postaci ze świata swej książki, jednakże pierwsze jej wydanie, gdy się już fi-
zycznie ukazało, stało się świadectwem bolesnych ran zadanych owej inwencji zarówno wskutek
błędów drukarskich, jak i niefortunnej (choć zapewne w dobrej wierze przeprowadzonej) korekty
redaktorskiej — i tak na przykład, ku przerażeniu Tolkiena, każde wystąpienie słowa dwarves („kra-
snoludy") i elven („elfowie" — szacowna forma męskoosobowa) korygowane było do postaci
(odpowiednio) dwarfs („krasnoludki") i elf in („elfetka" — domyślnie mały skrzydlaty potworek).
W 1965 roku amerykańskie wydawnictwo Ace Books wypuściło nieautoryzowane wydanie książki
w przekonaniu, że w USA nie obowiązuje brytyjski copyright; Tolkien pracował jednak nad korektą
i wkrótce (w październiku) nakładem Ballantine Books ukazało się autoryzowane wydanie rękopisu,
w miękkiej oprawie. Pierwszy zbiór poprawek Tolkiena uwzględniony został w drugim wydania
Ballantine Books, drugi zbiór poprawek znalazł odzwierciedlenie w wydaniach trzecim i czwartym
(wszystkie w miękkiej oprawie) tegoż wydawnictwa. Jednak poprawki autora nigdy nie zostały po-
prawnie naniesione i w tekście pojawiały się coraz to nowe błędy.

W Wielkiej Brytanii wydawnictwo Allen & Unwin opublikowało w październiku 1966 zrewidowany
rękopis, w twardej oprawie. Nie mogło ono jednak wykorzystać oryginalnego rękopisu z załącznikami,
gdyż ten po prostu zaginął; na potrzeby załączników posłużyło się więc wczesną kopią używaną
przez Ballantine Books — ta jednak nie zawierała drugiego zbioru poprawek, a co gorsza, w druku
wprowadzonych zostało mnóstwo błędów. Latem 1966 roku Tolkien dokonał ponownej rewizji tekstu,
jednakże w czerwcu Allen & Unwin poinformowało, że jest zbyt późno, by dokonane poprawki włączyć
do nowego wydania. I tak oto pod koniec roku 1966 w obiegu istniało wiele znacząco różnych i nie-
spójnych ze sobą wersji Władcy Pierścieni, z których żadna w pełni nie oddawała intencji Tolkiena.

Informacja na podstawie przypisu Douglasa A. Andersona do książki [Tolkien, 1995]


10.2. O odwzorowywaniu ogólnie 447

Każde wydanie Władcy Pierścieni wiązało się z szeregiem transformacji, jakich doznawał
tekst książki na drodze od rękopisu do wydrukowanej książki. Autor najpierw przepisywał rę-
kopis na maszynie i przesyłał wydawcy. Maszynopis poddawany był korekcie i redagowany
przed przekazaniem do składu w drukarni. Składanie tekstu wiązało się z wprowadzaniem do
niego błędów wynikających zarówno z ludzkiej omylności („literówek"), jak i podejmowanych
w dobrej wierze, choć często opacznie, zabiegów korektorskich. Jeżeli dodać do tego błędy po-
wstałe w trakcie samego drukowania — takie jak niewłaściwa kolejność lub błędna orientacja
stron — to mamy pełnię obrazu rzeczonej transformacji. I choć obecnie procedura wydawni-
cza jest wysoce zautomatyzowana, wciąż mogą wkradać się do niej błędy popełnione przez
autora, wydawcę lub drukarza — a ich poprawianie stanowi doskonałą okazję do wprowa-
dzania nowych błędów.
Mimo iż niektóre etapy procesu wydawniczego — takie jak przepisywanie rękopisu —
mogą się wydać mało wyzywające intelektualnie i są z natury proste, jednak ich monotonny
charakter i rozmiar (sześć tomów Władcy Pierścieni liczy 1000 z okładem stron) uniemożliwia
wręcz ustrzeżenie się wszelkich błędów.
Tworzenie i implementowanie modelu projektu obiektów w podobny sposób łączy się
z wieloma transformacjami. Przykładowo programiści dokonują lokalnych usprawnień mo-
delu pod kątem poprawy jego modularności i wydajności; dokonują także przekształcania
skojarzeń obecnych w modelu na referencje między obiektami, języki programowania nie wy-
kazują bowiem żadnego wsparcia dla koncepcji skojarzeń. Większości języków programowania
obca jest też idea kontraktów, programiści muszą więc implementować we własnym zakresie
weryfikację ich spełnienia. Także starannie zdefiniowane interfejsy nie są bytem niezmiennym,
bo zmieniające się wymagania klienta mogą prowadzić do ich modyfikowania — czynności
pociągającej intelektualnie zdecydowanie mniej niż na przykład analiza wymagań czy pro-
jektowanie systemu, lecz koniecznej do wykonania, mechanicznej, powtarzalnej i męczącej,
gdy wziąć pod uwagę jej skalę. Co gorsza, nie da się ona zbyt dalece automatyzować, bo wy-
maga podejmowania decyzji, których nie sposób rozstrzygać w sposób algorytmiczny.
Każda ze wspomnianych transformacji niesie ze sobą ryzyko popełnienia błędów, wy-
krywanych później na etapie testowania. Przedstawimy teraz kilka technik (ilustrowanych
stosownymi przykładami) umożliwiających redukowanie tego ryzyka.

10.2. O od wzo ro wywa n i u ogólnie


Celem transformacji modelu jest usprawnienie w jednym aspekcie (na przykład modularno-
ści) przy zachowaniu wszystkich pozostałych jego cech (między innymi elementów funkcjo-
nalnych). Transformacje mają więc z reguły charakter zlokalizowany, każda z nich obejmuje
niewielką liczbę klas, atrybutów lub operacji i wykonywana jest jak szereg niewielkich kroków.
Transformacje takie wykonywane są w ramach rozmaitych aktywności związanych z projek-
towaniem i implementowaniem obiektów — rozpatrzymy szczegółowo następujące z nich.

• Optymalizowanie modelu (patrz sekcja 10.4.1) zmierza do spełnienia wymagań


wydajnościowych stawianych temu modelowi. Przeprowadzane jest między innymi
w postaci redukowania krotności skojarzeń oraz dodawania skojarzeń redundantnych,
co ma przyspieszyć realizowanie żądań, a także dodawanie redundantnych atrybutów
w celu skrócenia czasu dostępu do obiektów.
448 Rozdział 10. • Odwzorowywanie modelu na kod

• Realizowanie skojarzeń (patrz sekcja 10.4.2), czyli odwzorowywanie skojarzeń w in-


strukcje i struktury danych, głównie referencje i ich kolekcje.
• Definiowanie wyjątków związanych z naruszeniem kontraktów (patrz sekcja 10.4.3)
to programowanie zachowania metod w sytuacji, gdy ich wywołanie odbywa się
z naruszeniem kontraktu zdefiniowanego dla tego wywołania. W praktyce oznacza
to generowanie wyjątków w wywoływanych metodach i ich obsługiwanie na wyż-
szym szczeblu hierarchii systemu.
• Odwzorowywanie modeli klas w schematy przechowywania danych (patrz sekcja
10.4.4), czyli wybór strategii magazynowania trwałych danych — niezależnych plików,
systemu bazy danych lub rozwiązania pośredniego między tymi dwoma. Poszcze-
gólne atrybuty klas i skojarzenia między klasami odwzorowywane są w nazwy plików
lub schematy relacyjnych baz danych.

10.3. Koncepcje odwzorowywania


Rozróżniamy cztery typy transformacji (patrz rysunek 10.1).

Rysunek 10.1. Cztery typy transformacji opisywane w tym rozdziale, czyli transformacja modelu,
refaktoryzacja kodu, inżynieria postępująca i inżynieria odwracająca

• Transformacja modelu (patrz sekcja 10.3.1) ograniczona jest do samego modelu.


Przykładem takiej transformacji jest konwersja pojedynczego atrybutu (na przykład
adresu reprezentowanego jako łańcuch znaków) do postaci klasy (przechowującej
pod postacią swych atrybutów poszczególne elementy adresu: ulicę, kod pocztowy
i miasto).
• Refaktoryzacja kodu (patrz sekcja 10.3.2) to modyfikowanie kodu źródłowego,
bez zmian w jego funkcjonalności. Podobnie jak transformacja modelu, refakto-
ryzacja obejmuje wybrany aspekt kodu, przy zachowaniu jego pozostałych cech.
• Inżynieria postępująca (patrz sekcja 10.3.3) to tworzenie szablonów kodu źró-
dłowego odpowiadającego modelowi obiektowemu. Wiele elementów modelu,
takich jak specyfikacje atrybutów skojarzeń, może być w ten sposób mechanicznie
odwzorowanych w kod — niektóre środowiska programistyczne udostępniają
mechanizmy automatycznego generowania kodu na tej zasadzie. Wygenerowane
szablony muszą być przez programistów uzupełnione o treści („ciała") metod oraz
definicje metod prywatnych.
10.4. Aktywności odwzorowywania 449

• Inżynieria odwracająca (patrz sekcja 10.3.4) to odtwarzanie modelu odpowiada-


jącego istniejącemu kodowi źródłowemu. Technika ta wykorzystywana jest w sytu-
acji, gdy model systemu nie istnieje (bo nigdy nie istniał albo został zagubiony).
Mimo iż niektóre narzędzia CASE udostępniają narzędzia inżynierii odwracającej,
wymaga ona znacznego udziału intelektu, bowiem kod źródłowy nie zawiera wszyst-
kich informacji umożliwiających odtworzenie modelu w sposób jednoznaczny.

10.3.1. Transformowanie modelu


Transformacja modelu jest operacją wykonywaną na istniejącym modelu, dającą w wyniku
inny model, o czym piszą M. Błaha i W. Premerlani [Błaha i Premerlani, 1998]. Jej celem jest
uproszczenie lub zoptymalizowanie istniejącego modelu pod kątem lepszej zgodności z wy-
maganiami zawartymi w specyfikacji. Może ona obejmować dodawanie, usuwanie lub prze-
mianowywanie klas, operacji, skojarzeń i atrybutów, w rezultacie model zostanie wzbogacony
albo zubożony o pewną informację.
W rozdziale 5. „Analiza wymagań" przeprowadzaliśmy już transformację tego typu,
polegającą na zorganizowaniu obiektów w hierarchę dziedziczenia i powodującą usunięcie
z modelu informacji redundantnych. Na rysunku 10.2 widoczny jest kolejny przykład takiej
transformacji, dotyczącej obiektów systemu ARENA: ponieważ wszystkie obiekty reprezentują-
ce graczy (klasa PI ayer), r e k l a m o d a w c ó w (Ad vert i ser) i k a p i t a n ó w lig (LeageuOwner) posia-
dają atrybut email reprezentujący adres e-mail, zdefiniowana zostaje dla nich superklasa
User, reprezentująca użytkownika dysponującego adresem e-mail.

Rysunek 10.2. Eliminacja redundantnego atrybutu poprzez przeniesienie go do wspólnej superklasy

Z reguły każde tworzenie systemu może być traktowane jak ciąg transformacji modelu
analitycznego dający w efekcie model projektu obiektów, który następnie uzupełniany jest suk-
cesywnie obiektami realizacyjnymi. Jakkolwiek transformacja modelu jest czynnością w dużym
stopniu mechaniczną, właściwy wybór transformacji dla określonego zbioru klas wymaga du-
żego doświadczenia.
450 Rozdział 10. • Odwzorowywanie modelu na kod

10.3.2. Refaktoryzacja
Refaktoryzacja kodu ma na celu polepszenie jego czytelności lub modyfikowalności, bez wpływu
na funkcjonalność, o czym pisze M. Fowler [Fowler, 2000]. Refaktoryzacja koncentruje się
zwykle na specyficznym polu lub metodzie klasy; aby wyeliminować ryzyko niezamierzonego
wprowadzenia zmian w zachowaniu systemu, refaktoryzację przeprowadza się zwykle jako
serię prostych kroków, z których każdy weryfikowany jest za pomocą odpowiednich testów
(refaktoryzacja przeplata się więc z testowaniem). Istnienie zestawu testowego dla każdej klasy
pozwala programistom z jednej strony, nabierać zaufania do poprawności przeprowadzanych
przez siebie zabiegów, z drugiej natomiast, zachęca ich do pozostawiania interfejsów w nie-
zmienionym stanie bądź ewentualnie modyfikowania ich tylko w takim stopniu, jaki naprawdę
jest konieczny.
I tak na przykład transformacja przedstawiona na rysunku 10.2 przekłada się na trzy
refaktoryzacje. Pierwsza obejmuje wciągnięcie pola (Pull Up Field) z subklas do wspólnej su-
perklasy; druga — wciągnięcie treści konstruktora (Pull Up Constructor Body) — przenosi
z subklas do superklasy kod inicjujący to pole, trzecia wreszcie — wciągnięcie metod (Pull Up
Method) — polega na przeniesieniu z subklas do superklasy metod operujących na polu (atry-
bucie) emai 1. Przyjrzyjmy się szczegółowo wszystkim trzem.
Wciągnięcie pola, ukazane na listingu 10.1, obejmuje następujące kroki:

1. Przeanalizowanie klas PI ayer, LeagueOwner i Adverti ser w celu upewnienia się,


że wszystkie posiadają atrybut odpowiedzialny za reprezentowanie adresu e-mail
(w tym samym znaczeniu). Ponieważ atrybut ten ma w każdej z klas inną nazwę
(emai 1, eMai 1 i email_address), n a l e ż y n a z w ę tę u j e d n o l i c i ć (emai 1).

2. Zdefiniowanie publicznej klasy User.


3. Ustanowienie klasy User superklasą dla klas PI ayer, LeagueOwner i Adverti ser.
4. Dodanie do klasy User chronionego pola o nazwie emai 1.
5. U s u n i ę c i e p o l a emai 1 z klas PI ayer, LeagueOwner i Adverti ser.

6. Skompilowanie kodu i przetestowanie jego poprawności.

Listing 10.1. Wciągnięcie pola do superklasy jako etap refaktoryzacji

Przed refa kto ryza cją Po refaktoryzacji

public class PIayer { public class User {


private String emai1; protected String email;
}
} public class Player extends User {
public class LeagueOwner { }
private String eMail;
public class LeagueOwner extends User {
} }
public class Advertiser {
private String email_address; public class Advertiser extends User {
u...
} }
10.4. Aktywności odwzorowywania 451

Wciągnięcie treści konstruktora do superklasy (listing 10.2) wymaga wykonania nastę-


pujących kroków:

1. Dodania do superklasy User konstruktora User (Address email).


2. Przypisania polu ema i 1 wartości przekazanej jako parametr wywołania konstruktora.
3. Dodania w konstruktorze klasy PI ayer delegującego wywołania super (emai 1).
4. Skompilowania i przetestowania kodu.

5. Powtórzenia kroków od 2. do 4. dla klas LeagueOwner i Adverti ser.

Listing 10.2. Wciągnięcie treści konstruktora do superklasy jako etap refaktoryzacji


Przed refaktoryzacją Po refaktoryzacji

public class User { public class User {


private String email; public User(String emai1) {
} this.email = email;
}
}
public class Player extends User { public class Player extends User {
public Player(String emai1) { public Player(String email) {
this.email = email; super(email);

} }
} }
public class LeagueOwner extends User { public class LeagueOwner extends User {
public LeagueOwner(String email) { public LeagueOwner(String email) {
this.email = email; super(email);

} }
} }
public class Advertiser extends User { public class Advertiser extends User {
public Advertiser(String email) { public Advertiser(String emai1) {
this.email = email; super(email);

} }
} }

Uzyskaliśmy zatem zlokalizowane pole emai 1 i jego inicjację w klasie User. Kolejnym
krokiem będzie wyodrębnienie z klas PI ayer, LeagueOwner i Adverti ser metod operujących
na polu emai 1 i wciągnięcie tych metod do superklasy.

1. Analizując klasę PI ayer, zauważamy metodę not i fy (), operującą wyłącznie na polu
emai 1 i niewykonującą żadnych czynności specyficznych dla klasy PI ayer.
2. Kopiujemy metodę not i f y () do superklasy User i rekompilujemy kod.
3. Usuwamy metodę not i fy () z klasy PI ayer.
4. Kompilujemy i testujemy kod.
5. W analogiczny sposób identyfikujemy podobne metody w klasach LeagueOwner
i Adverti s e r i dla każdej z tych metod powtarzamy kroki od 2. do 4.
452 Rozdział 10. • Odwzorowywanie modelu na kod

Wykonanie wszystkich trzech etapów refaktoryzacji powoduje poddanie kodu systemu


ARENA transformacji podobnej do tej, jakiej poddano model obiektów tego systemu (patrz
rysunek 10.2). Zauważmy jednak, iż w porównaniu z transformowaniem modelu refaktoryza-
cja obejmuje więcej kroków i przeplatana jest testami. Wynika to z faktu, że kod źródłowy jest
tworem bardziej szczegółowym niż model i stąd bardziej podatnym na wprowadzanie błędów.
W następnej sekcji omówimy generalne zasady unikania błędów transformacji.

10.3.3. Inżynieria postępująca


Inżynieria postępująca (forward endineering) wykonywana jest na podstawie elementów mo-
delu, a jej wynikiem jest zbiór instrukcji kodu źródłowego, obejmujących między innymi de-
klaracje klas, wyrażenia języka Java i schematy baz danych. Podstawowym celem inżynierii
postępującej jest utrzymywanie ścisłej odpowiedniości między modelem projektu obiektów
a powiązanym z nim kodem i redukowanie błędów wprowadzanych do kodu podczas jego im-
plementowania (a co za tym idzie, zmniejszenie czasochłonności tego implementowania).
Przykład zastosowania inżynierii postępującej widzimy na rysunku 10.3, przedstawiają-
cym wynik transformacji modelu UML klas User i LeagueOwner w ich kod źródłowy. Najpierw
każda z klas UML przekształcana jest na klasę w języku Java, następnie relacja generalizacji
odzwierciedlana jest przez słowo kluczowe extends tegoż języka. Na końcu każdy atrybut
z obu klas UML odwzorowany jest na odpowiednie pole prywatne, któremu towarzyszą dwie
publiczne metody dostępowe, przeznaczone do odczytywania i modyfikowania jego zawartości.
Programiści — oczywiście — mogą rozwijać ów wynik transformacji i wprowadzać nowe
elementy, na przykład metodę setMaxNumLeagues, przypisującą wartość polu maxNumLeagues,
można wzbogacić o sprawdzanie, czy wartość ta jest całkowitą liczbą dodatnią.
Zauważmy, że — z wyjątkiem nazw atrybutów i metod — kod powstający w wyniku
transformacji obu klas jest jednakowy. Ułatwia to programistom rozpoznawanie śladów trans-
formacji w kodzie źródłowym i zachęca ich do stosowania odpowiednich konwencji nazewni-
czych. Co więcej, ponieważ programiści dokonują realizacji klas w sposób spójny, zmniejsza
się ryzyko popełniania błędów przy tej realizacji.

10.3.4. Inżynieria odwracająca


Przedmiotem działań inżynierii odwracającej (reverse engineering) jest zbiór elementów kodu
programu, a wynikiem — zbiór elementów modelu. Odtwarzanie modelu dla istniejącego
systemu może być spowodowane bądź tym, że uległ on zagubieniu albo po prostu nigdy nie
istniał, bądź zatraceniem synchronizacji między istniejącym modelem a kodem źródłowym
programu. Inżynieria odwracająca, zgodnie z tym, co sugeruje jej nazwa, obejmuje działania
odwrotne w stosunku do inżynierii postępującej: dla każdego pola klasy w kodzie źródłowym
tworzony jest atrybut klasy w modelu UML, każdej zaś metodzie z kodu źródłowego przypo-
rządkowana jest operacja klasy modelu. Ponieważ jednak inżynieria postępująca wiąże się
z utratą części informacji, między innymi w wyniku konwersji skojarzeń na referencje i ich
kolekcje, rezultatem inżynierii odwracającej niekoniecznie musi być ten sam model, na pod-
stawie którego (w ramach inżynierii postępującej) wygenerowany został kod źródłowy. A zatem,
mimo iż wiele narzędzi C A S E udostępnia narzędzia inżynierii odwracającej, rezultatem ich
zastosowania może być w najlepszym razie jedynie dobre przybliżenie oryginalnego modelu.
10.4. Aktywności odwzorowywania 453

Model projektu obiektów przed transformacją

User LeagueOwner
+email:String +niaxNumLeagues:1nt
+notify(msg:String)

Kod źródłowy po transformacji

public class User { public class LeagueOwner extends


private String emai1; User {
private int maxNumLeagues;
public String getEmai1() {
return emai 1; ic int getMaxNumLeagues
public getMaxNumLeaguesQ
return maxNumLeagues;

public void setEmai1(String value) {


emai1 = value; public void setMaxNumLeagues(int
value) {
maxNumLeagues = value;
public void notify(String msg)

.... inne metody....

.... inne metody....

Rysunek 10.3. Realizacja klas User i LeagueOwner w postaci diagramu klas UML i fragmentów kodu
w języka Java. W wyniku transformacji publiczna widzialność atrybutów emai 1 i maxNumLeagues od-
zwierciedlona zostaje w postaci par publicznych metod przeznaczonych do odczytywania i modyfikowa-
nia wartości tych atrybutów. Same atrybuty reprezentowane są natomiast w postaci pól prywatnych

10.3.5. Zasady transformacji


Celem wszystkich opisywanych transformacji jest ulepszanie systemu pod względem pewnych
kryteriów: transformacja modelu zmierza do poprawienia jego zgodności z celami projekto-
wymi, refaktoryzacja poprawia czytelność i modyfikowalność kodu źródłowego, inżynieria
postępująca ułatwia zachowanie spójności między modelem a kodem źródłowym, natomiast
inżynieria odwracająca pomaga w odtworzeniu oryginalnego modelu na bazie jego obrazu, ja-
kim j est kod źródłowy.
Oczywiście, jak w każdej modyfikacji modelu czy kodu, programiści próbujący popra-
wić je ryzykują wprowadzenie błędów trudnych do wykrycia i naprawienia. By zminimalizo-
wać takie ryzyko, poszczególne transformacje powinno się przeprowadzać przy zachowaniu
następujących reguł.

• Każda transformacja powinna dotyczyć tylko jednego, ściśle określonego kryterium.


Usprawnianie systemu powinno być ukierunkowane na jeden i tylko jeden cel pro-
jektowy, i tak na przykład jedna transformacja może mieć na celu skrócenie czasu
454 Rozdział 10. • Odwzorowywanie modelu na kod

reakcji, inna natomiast — eliminację redundancji z kodu źródłowego. Próba opty-


malizowania systemu pod kątem kilku kryteriów jednocześnie zwiększa złożoność
kodu i sprzyja wprowadzaniu nowych błędów.
• Każda transformacja musi mieć charakter lokalny, czyli obejmować niewielką liczbę
metod lub klas. Przedmiotem transformacji są zwykle implementacje metod, wów-
czas pozostaje ona bez wpływu na kod wywołujący te metody. Jeśli jednak transfor-
macja wiąże się ze zmianami w interfejsie (na przykład dodawaniem parametru do
metody), klasy klienckie powinny być do nowej metody przystosowywane stopniowo,
po jednej na raz: w modyfikowanej klasie należy tymczasowo pozostawić obie wersje
metody i kolejno „przełączać" poszczególne klasy klienckie ze starej wersji na nową
— należy bowiem przez cały czas zachować możliwość kompilowania kodu i jego
testowania. Jeżeli transformacja obejmuje na raz kilka podsystemów, nie jest to tak
naprawdę transformacja, a raczej zmiana architektury modelu.
• Każda transformacja powinna być izolowana od innych zmian. W celu zapewnienia
jeszcze lepszej lokalizacji zmian transformacje powinny być wykonywane pojedyn-
czo — i tak na przykład poprawiając wydajność metody, nie można wzbogacać jej
o nową funkcjonalność; dodając nową funkcjonalność, należy unikać optymalizo-
wania istniejącego kodu. Skupienie się na mniejszym zbiorze problemów zmniejsza
ryzyko popełniania błędów.
• Każda transformacja musi być poddana weryfikacji. Jeśli nawet poszczególne
transformacje mają charakter czysto mechaniczny, wykonywane są przez ludzi.
Po zakończeniu jednej transformacji, a przed rozpoczęciem następnej, należy
wykonać walidację wprowadzonych zmian. I tak na przykład po transformacji
modelu obiektowego należy odpowiednio zaktualizować diagram sekwencji obej-
mujący zmienione klasy modelu: przegląd przypadków użycia związanych z tym
diagramem może pomóc w wykryciu ewentualnych błędów. Walidacja refaktory-
zacji kodu sprowadza się do uruchomienia odpowiednich przypadków testowych,
a być może także wiąże się z koniecznością opracowania nowych (na przykład
w konsekwencji dodania nowego kodu obsługującego sytuacje wyjątkowe). Jeżeli
już bowiem popełnia się błędy, najłatwiej je wykrywać i modyfikować jak naj-
wcześniej.

10.4. Aktywności odwzorowywania


Aby zilustrować opisane zasady, zaprezentujemy praktyczne przykłady najczęściej wykony-
wanych transformacji, takie jak:

• Optymalizowanie modelu obiektowego (patrz sekcja 10.4.1),


• Odwzorowywanie skojarzeń na kolekcje (patrz sekcja 10.4.2),
• Odwzorowywanie kontraktów w wyjątki (patrz sekcja 10.4.3),
• Odwzorowywanie modelu obiektowego w schematy bazy danych (patrz sekcja 10.4.4).
10.4. Aktywności odwzorowywania 455

10.4.1. Optymalizowanie modelu obiektowego


Bezpośrednia translacja modelu analitycznego na kod źródłowy zwykle nie daje zadowa-
lających rezultatów. Model analityczny skupia się bowiem na funkcjonalności systemu i kom-
pletnie ignoruje kwestie, które stają się istotne na etapie projektowania tegoż systemu i które
odzwierciedlone zostają w jego celach projektowych: mowa tu między innymi o optymalizacji
czasu reakcji, czasu wykonywania obliczeń czy też o zapotrzebowaniu na pamięć. Przykładowo
strona HTML wyświetlana w przeglądarce W W W może być w modelu analitycznym repre-
zentowana jako dokument agregujący tekst i obrazki, jednakże dla wygody użytkownika, za-
interesowanego jak najwcześniejszym wyświetlaniem dostępnych szczegółów strony, możemy
zadecydować o pokazywaniu zastępników dla obrazków, które jeszcze nie zdążyły się załadować
— z każdym obrazkiem należy wówczas skojarzyć odpowiedni obiekt proxy.
W tej sekcji opiszemy cztery proste, lecz często wykonywane optymalizacje tego rodzaju:
optymalizowanie ścieżek dostępu przez dodawanie nowych skojarzeń, redukowanie (kolapsację)
obiektów do atrybutów, opóźnianie wykonywania kosztownych operacji i cache owanie wy-
ników czasochłonnych obliczeń. Programista rozważający optymalizowanie modelu musi
mieć świadomość, że każda optymalizacja wiąże się z kompromisem między wydajnością kodu
a jego czytelnością.

Optymalizowanie ścieżek dostępu

Jednym z powszechnych źródeł nieefektywności programów jest wielokrotne trawerso-


wanie skojarzeń wielopoziomowych, skojarzeń o krotności „wiele" oraz odwoływanie się do
niewłaściwie zlokalizowanych atrybutów, o czym piszą J. Rumbaugh, M. Błaha, W. Premerlani,
F. Eddy i W. Lorensen [Rumbaugh i in., 1991].

• Powtarzane trawersowanie skojarzeń. Analizując — przy użyciu diagramu sekwencji


— często wykonywane operacje, można wśród nich zauważyć takie, których wyko-
nywanie wiąże się z wielokrotnym przemierzaniem (trawersowaniem) tych samych,
wielopoziomowych skojarzeń. Ze względów wydajności w często wykonywanych
operacjach powinno się unikać tego typu czasochłonnych czynności na rzecz bezpo-
średnich powiązań między obiektem formułującym żądanie a obiektem, który je
realizuje. Jeśli takie powiązanie nie istnieje już w modelu, należy je utworzyć (bez
względu na fakt, że prawdopodobnie wprowadza się w ten sposób redundancję do
modelu — jest ona ceną płaconą za większą wydajność). W istniejących systemach,
„przerabianych" w ramach inżynierii wtórnej, identyfikowanie takich „kosztownych"
ścieżek jest łatwiejsze niż w przy tworzeniu systemów „od zera" — w tym drugim
przypadku należy powstrzymać się z dodawaniem redundantnych skojarzeń, aż do
otrzymania miarodajnych wyników dynamicznej analizy systemu, na przykład pod-
czas testowania, i zident/iikovvania tych skojarzeń, które są najczęstszą przyczyną
powstawania „wąskicl garJji".
• Skojarzenia wielokrotne. /V przypadku skojarzeń „jeden na wiele" i „wiele na wiele"
można spróbować zastąpić krotność „wiele" krotnością „jeden", na przykład za po-
mocą kwalifikowania skojarzeń (patrz sekcja 2.4.2). Gdy redukcja krotności nie jest
możliwa, można zastosować uporządkowanie lub indeksowanie obiektów po stronie
„wiele" w celu skrócenia czasu dostępu do konkretnego obiektu.
456 Rozdział 10. • Odwzorowywanie modelu na kod

• Źle zlokalizowane atrybuty. Jednym z przejawów „przemodelowania" systemu jest


duża liczba prostych klas — tak prostych, że niewykazujących żadnych objawów za-
chowania, a jedynie przechowujących jeden lub kilka atrybutów, wykorzystywanych
przez pojedyncze klasy. Każdy z takich atrybutów kwalifikuje się do przeniesienia na
grunt klasy wywołującej, a wtedy odwołania do niego będą miały charakter lokalny.
Po wykonaniu serii takich operacji może się okazać, że niektóre klasy straciły rację
bytu i można je z modelu po prostu usunąć.

Systematyczne przejrzenie modelu pod kątem trzech wymienionych kryteriów da zatem


w wyniku model z dodatkowymi redundantnymi skojarzeniami, mniejszą liczbą nieefektywnych
skojarzeń „wiele na wiele" i mniejszą liczbą klas.

Kolapsacja — redukowanie obiektów do atrybutów

Po kilkakrotnej restrukturyzacji i optymalizacji projektu niektóre z jego klas mogą zostać


zredukowane do tego stopnia, że stanowić będą jedynie zbiory atrybutów, nie przejawiając
żadnych objawów charakterystycznego zachowania. Taka klasa, jeśli skojarzona jest z inną
i tylko jedną, może być przekształcona na jej atrybuty — taka operacja nazywa się kolapsacją
(klasa niejako „zapada się", kurczy do postaci swych atrybutów).
W diagramie na rysunku 10.4 taką klasą jest Social Security. Reprezentuje ona ubez-
pieczenie socjalne pracownika prezentowanego przez obiekt skojarzonej klasy Person i posiada
tylko jeden atrybut (number) będący numerem tego ubezpieczenia. Jeśli żaden z przypadków
użycia nie przypisuje klasie Soci al Securi ty określonego zachowania i jeżeli jedyną skojarzo-
ną z nią klasą jest Person, można klasę Soci al Securi ty zastąpić stosownym atrybutem klasy
Person (na rysunku atrybut ten nosi nazwę SSN).

Rysunek 10.4. Przykład kolapsacji klasy

Decyzja o kolapsacji danej klasy nie zawsze jest decyzją oczywistą. Z klasą Soci al Securi ty
mogły być na przykład skojarzone operacje generowania unikalnych identyfikatorów w opar-
ciu o numer ubezpieczenia i specyfikę komputera, na którym ma być docelowo eksploatowany
system. Ogólnie zatem programiści powinni powstrzymywać się z kolapsacją, aż do czasu roz-
poczęcia implementowania danej klasy, kiedy to zakres jej odpowiedzialności stanie się w pełni
oczywisty. Ponieważ istnieć już będzie najprawdopodobniej duża porcja kodu, w następstwie
kolapsacji klasy może być konieczna jego refaktoryzacja.
10.4. Aktywności odwzorowywania 457

W książce M. Fowlera [Fowler, 2000] opisana jest metoda transformacji kodu źródłowego,
stanowiąca odpowiednik kolapsacji wykonywanej na modelu obiektów, a nazywana popularnie
wchłonięciem klasy (w oryginale Inline Class Refactoring). Jej wykonanie przekłada się na na-
stępujący ciąg kroków:

1. Zadeklarowanie w klasie docelowej (tu: Person) pól i metod odpowiadających polom


i metodom klasy źródłowej (tu: So ci al Securi ty).
2. Zamiana wszystkich odwołań do klasy źródłowej na odwołania do klasy docelowej.
3. Zmiana nazwy klasy źródłowej na jakąś unikalną nazwę, niewystępującą w kodzie.
4. Uruchomienie kompilacji programu, która wskazać może przeoczone dotąd od-
wołania do klasy źródłowej.
5. Po ostatecznym usunięciu z kodu odwołań do klasy źródłowej skompilowanie go
i przetestowanie.
6. Usunięcie definicji klasy źródłowej z kodu.

Opóźnianie kosztownych obliczeń

Tworzenie niektórych obiektów może być kosztowne zarówno w aspekcie czasowym,


jak i w aspekcie wykorzystywania zasobów systemu (między innymi pamięci). W charakterze
przykładu przywołajmy znajomy system ARENA i przyjrzyjmy się obiektowi Advertisement
^Banner reprezentującemu baner reklamowy wyświetlany na ekranie komputera gracza.
Wczytywanie obrazka baneru, piksel o pikselu, z pliku dyskowego jest czasochłonne, może
być jednak odłożone do momentu, gdy obrazek ten będzie rzeczywiście potrzebny, czyli do
chwili, w której nastąpić ma jego wyświetlenie. Tego typu optymalizację przeprowadzić można
przy użyciu wzorca projektowego Proxy (patrz sekcja A.8 i książka E. Gammy i współautorów
[Gamma i in., 1994]). Obiekt proxy (ImageProxy) zajmuje miejsce oryginalnego obrazka
(Image), prezentując przy tym jego niezmieniony interfejs (patrz rysunek 10.5). Operacje
odnoszące się do ogólnych właściwości obrazka, takich jak szerokość (widthQ) i wysokość
(heightO), nie wymagają jego fizycznej obecności i wykonywane są w całości przez obiekt
proxy; gdy jednak przychodzi do wyświetlenia obrazka (to znaczy gdy po raz pierwszy wywo-
łana zostanie operacja paint ()), obiekt ImageProxy wczytuje jego obraz z pliku i tworzy na
jego podstawie obiekt klasy Real Image. Jeśli klient nie wywoła w ogóle operacji pai nt (), za-
oszczędzimy sobie fatygi związanej ze zbędnym ładowaniem obrazka. Oczywiście dla klasy
klienta opisany mechanizm jest całkowicie nieistotny — komunikuje się ona z nim za pośred-
nictwem interfejsu identycznego z interfejsem klasy Image i może nie być świadoma faktu, że
ma do czynienia z obiektem proxy.

Cache1ówanie wyników czasochłonnych obliczeń

Niektóre metody, wywoływane bardzo wiele razy, zwracają wynik bazujący na warto-
ściach, które zmieniają się bardzo rzadko albo nie zmieniają się wcale. W metodach tego ro-
dzaju kryją się potężne nieraz możliwości poprawy wydajności systemu: niezmieniającą się
wartość można obliczyć jeden raz i każdorazowo, gdy będzie potrzebna w przyszłości, odwo-
ływać się do zapamiętanego wyniku (przy okazji jednak pilnując jego aktualności). Jako przykład
458 Rozdział 10. • Odwzorowywanie modelu na kod

Rysunek 10.5. Opóźnianie kosztownych operacji przy użyciu wzorca projektowego Proxy

— znowu z systemu ARENA — rozpatrzmy operację LeagueBoundary.getStatistics(),


udostępniającą statystykę odnoszącą się do wszystkich graczy będących członkami ligi i do
wszystkich turniejów organizowanych przez tę ligę. Statystyka ta zmienia się po zakończeniu
meczu (Match) i w żadnej innej sytuacji, nie ma więc potrzeby obliczania jej za każdym razem,
gdy użytkownik (User) będzie chciał ją obejrzeć. W zamian statystyka ta może być przecho-
wywana w formie tymczasowego obiektu cacheującego skojarzonego z ligą (League), który
uznany zostaje za nieaktualny w momencie zakończenia dowolnego meczu (Match) wchodzą-
cego w skład dowolnego turnieju (Tournament) organizowanego przez tę ligę (co przy najbliż-
szym żądaniu statystyki powoduje konieczność ponownego jej obliczenia i zapamiętania we
wspomnianym obiekcie, który przez to znów staje się aktualny). Zauważmy, że ceną płaconą
tu za zwiększenie wydajności jest dodatkowe zapotrzebowanie na pamięć ze strony wspo-
mnianego obiektu cache'ującego.

10.4.2. Odwzorowywanie skojarzeń na kolekcje


Skojarzenie to koncepcja UML oznaczająca ogólnie kolekcję dwukierunkowych powiązań
między dwoma obiektami (lub większą ich liczbą). Języki programowania, nawet zorientowane
obiektowo, nie znają jednak koncepcji skojarzeń; w zamian posługują się referencjami, za po-
mocą których odzwierciedlane jest odniesienie jednego obiektu do innego, bądź kolekcjami
(być może uporządkowanymi) takich referencji. Referencja jest jednokierunkowa i zawsze
wiąże dokładnie dwa obiekty. Na etapie projektowania obiektów skojarzenia wyrażane są
w kategoriach referencji, przy uwzględnieniu krotności i kierunku owych skojarzeń.
10.4. Aktywności odwzorowywania 459

Warto zauważyć, że wiele narzędzi opartych na UML wykonuje mechanicznie transfor-


mację skojarzeń na referencje; mimo to, niezmiernie ważne jest zrozumienie każdej z po-
wstałych w ten sposób referencji, wszak bez tego nie sposób odpowiedzialnie operować
kodem źródłowym.

Jednokierunkowe skojarzenia „jeden do jednego". Są to najprostsze ze skojarzeń, w systemie


ARENA przykładem takiego skojarzenia może być skojarzenie reklamodawcy (Adverti ser)
z jego kontem (Account), które odpowiedzialne jest za śledzenie wszystkich konsekwencji płat-
niczych związanych z wyświetlaniem banerów reklamowych (AdvertisementBanner). Skoja-
rzenie to jest jednokierunkowe z tego względu, że w klasie Adverti ser wywoływane są opera-
cje klasy Account, lecz klasa Account nie odwołuje się do żadnych operacji ani atrybutów klasy
Adverti ser. Jedynym reprezentantem skojarzenia jest więc atrybut account (typu Account)
w klasie Adverti ser. Sytuację tę przedstawiono schematycznie na rysunku 10.6.

Kod źródłowy po transformacji

public class A d v e r t i s e r {
private Account account;
public A d v e r t i s e r ( ) {
account = new Account();
}
public Account getAccount() {
return a c c o u n t ;
}
}

Rysunek 10.6. Przykład realizacji jednokierunkowego skojarzenia „jeden do jednego"

Wspomniane skojarzenie przekłada się na nadanie właściwej wartości polu account


w obiekcie klasy Adverti ser: ponieważ z każdym reklamodawcą skojarzone jest dokładnie jedno
konto, pole to może mieć wartość pustą (nul 1) jedynie w czasie tworzenia obiektu Adverti ser,
w innych okolicznościach musi zawierać niepustą wartość stanowiącą referencję do odnośnego
obiektu klasy Account. Ponieważ wartość ta nie zmienia się w czasie, uczynimy pole account
polem prywatnym, a jego wartość odczytywać będziemy za pomocą metody A d v e r t i s e r ,
^ g e t Account (); uchroni to wspomniane pole przed zmodyfikowaniem (nawet niezamie-
rzonym).

Dwukierunkowe skojarzenia „jeden do jednego". Kierunkowość skojarzeń może zmieniać


się w czasie projektowania systemu. Skojarzenia dwukierunkowe są bardziej skompliko-
wane od jednokierunkowych, wprowadzają bowiem wzajemne zależności między obiektami.
Załóżmy na przykład, że każde konto (reprezentowane przez obiekt klasy Account) opatrujemy
460 Rozdział 10. • Odwzorowywanie modelu na kod

nazwą zależną od macierzystego reklamodawcy: metoda klasy Account () generująca tę nazwę


będzie więc musiała odwoływać się do skojarzonej z nią klasy Adverti ser, co skojarzeniu
między klasami nadaje charakter dwukierunkowy (patrz rysunek 10.7). W związku z tym,
w klasie Account pojawia się atrybut (pole) owner, co automatycznie pociąga za sobą poważne
konsekwencje: ponieważ w ten sposób wprowadzamy do modelu redundancję, musimy upew-
nić się, że w skojarzonych obiektach klas Adverti ser i Account zawartość pól (odpowiednio)
account i owner jest spójna — każde z nich zawiera referencję do skojarzonego obiektu. Nie-
stety, języki programowania nie dostarczają żadnych narzędzi do wymuszania spójności tego
typu ani też do klarownego zaznaczania, że mamy do czynienia z realizacją skojarzenia
dwukierunkowego. Ten drugi brak możemy zrekompensować za pomocą odpowiednich
komentarzy w każdej z klas, konsekwencje pierwszego musimy wziąć na swoje programi-
styczne barki, zapewniając nadawanie obu polom właściwych wartości i uniemożliwiając póź-
niejsze ich modyfikowanie.

public c l a s s Advertiser { public class Account {


/* Pole account jest inicjowane /* Pole owner jest inicjowane
* w niniejszym konstruktorze i nigdzie * w niniejszym konstruktorze i nigdzie
* indziej nie jest modyfikowane * indziej nie jest modyfikowane
V V
p r i v a t e Account account; private Adverti ser owner;

publ ic A d v e r t i s e r ( ) { public Account(Advertiser owner)


account = new Account(this); thi s.owner = owner;
}
public Account getAccount() publ ic Advertiser getOwnerQ
return a c c o u n t ; return owner;
} }

Rysunek 10.7. Przykład realizacji dwukierunkowego skojarzenia „jeden do jednego"

Nadawanie wartości obu polom odbywa się z inicjatywy konstruktora klasy Adverti ser:
do wywoływanego konstruktora klasy Account obiekt wywołujący przekazuje referencję do
samego siebie ( t h i s ) jako wartość przeznaczoną do przypisania polu owner, jednocześnie
obiekt ten przypisuje własnemu polu account referencję do obiektu utworzonego przez kon-
struktor klasy Account:

account = new Account(this);


Niemodyfikowalność obu pól zapewnimy w sposób oczywisty, deklarując je jako pola
prywatne i nie definiując żadnych operacji, które mogłyby zmieniać ich wartość.
10.4. Aktywności odwzorowywania 461

W układzie z rysunku 10.7 obie klasy są od siebie uzależnione również w tym sensie, że
w przypadku zmian w jednej z nich obie wymagają rekompilacji. W przypadku skojarzenia
jednokierunkowego związek ten był nieco luźniejszy — klasa Account była niewrażliwa na
zmiany w obrębie klasy Adverti ser. Wybór między użyciem skojarzenia jedno- albo dwukie-
runkowego jest zawsze uwarunkowany specyfiką konkretnego kontekstu; by ułatwić sobie
ewentualną zmianę między jednym a drugim, pozostawiamy ukryte oba atrybuty reprezentu-
jące skojarzenie, co sprawia, że stają się one osiągalne tylko za pośrednictwem metod dostępo-
wych get... (), co minimalizuje ewentualne zmiany w interfejsie API.

Skojarzenia „jeden na wiele". Skojarzenia „jeden na wiele" nie są realizowalne ani za pomocą
pojedynczej referencji, ani za pomocą pary referencji: po stronie „wiele" wystąpić musi
kolekcja referencji. Załóżmy, że w systemie ARENA jednemu reklamodawcy (Adverti ser)
przyporządkowano wiele kont (Account), z których każde dedykowane jest reklamowaniu
innego produktu. Skojarzenie klasy Adverti ser z klasą Account jest więc skojarzeniem typu
„jeden na wiele" (patrz rysunek 10.8). Ponieważ konta należące do jednego reklamodawcy nie
występują w jakiejś określonej kolejności i dane konto należy do co najwyżej jednego rekla-
modawcy, po stronie „wiele" skojarzenia użyjemy zbioru referencji, nazwanego accounts. Jako
że opisywane skojarzenie jest dwukierunkowe, stąd, oprócz oczywistych metod addAccount ()
i removeAccount () definiowanych w klasie Adverti ser i służących do modyfikowania kolekcji
accounts, w klasie Account pojawia się metoda setOwnerQ służąca przypisywaniu konta do
jego właściciela poprzez nadawanie odpowiedniej wartości polu owner.
Podobnie jak w przypadku skojarzenia „jeden do jednego", skojarzenia między obiek-
tami klas Adverti ser i Account muszą zostać zrealizowane podczas ich tworzenia. Ponieważ
jednak z danym obiektem Adverti ser może być skojarzona różna liczba obiektów Account,
za wywoływanie konstruktora klasy Account nie odpowiada obiekt klasy Adverti ser, a specjalny
obiekt sterujący, odpowiedzialny za tworzenie i archiwizowanie obiektów Account.
Zauważmy także, iż wybór konkretnego wariantu kolekcji po stronie „wiele" uwarun-
kowany jest ograniczeniem narzuconym na skojarzenie: ponieważ wśród obiektów Account
skojarzonych z konkretnym obiektem Adverti ser nie istnieje żadne szczególne uporząd-
kowanie, referencje do tych obiektów przechowywane są w formie zwykłego zbioru (HashSet);
gdyby jednak istniała wśród nich relacja porządkującą, musielibyśmy przechowywać je w postaci
listy (List). Aby uniezależnić interfejs od tej różnicy, zdefiniowaliśmy metodę getAccountsQ
tak, by zwracała wynik typu Col 1 e c t i on — to wspólna superklasa dla klas Li s t i Set.

Skojarzenia „wiele na wiele". Jak łatwo się domyślić, w tym przypadku obie strony skoja-
rzenia reprezentowane są w postaci kolekcji referencji, a odpowiednie operacje mają za
zadanie utrzymywanie spójności między tymi kolekcjami. Sięgnijmy znów do systemu ARENA,
gdzie w danym turnieju (Tournament) może być zarejestrowanych wielu graczy (PI ayer) i jed-
nocześnie dany gracz może być zarejestrowany w wielu turniejach. Obie wspomniane kolekcje
mają tym razem postać listy ( L i s t ) , a za zarządzanie nimi odpowiedzialne są metody
addPlayerQ, removePlayerQ, addTournamentQ i removeTournamentO (patrz rysunek 10.9);
dwie początkowe operacje zidentyfikowaliśmy w modelu projektu obiektów (patrz rysunek
9.10) z tą różnicą, że pierwsza z nich nosiła tam nazwę acceptPl ayer (), którą to nazwę zmie-
niliśmy tu na addPl ayer ()w celu utrzymania spójności z kodem generowanym dla innych
462 Rozdział 10. • Odwzorowywanie modelu na kod

Rysunek 10.8. Przykład realizacji dwukierunkowego skojarzenia „jeden na wiele"

Rysunek 10.9. Przykład realizacji dwukierunkowego skojarzenia „wiele na wiele"


10.4. Aktywności odwzorowywania 463

skojarzeń. Podobnie jak w poprzednim przykładzie, operacje te modyfikują kolekcje refe-


rencji w sposób zapewniający ich spójność (kolekcje te nie są dostępne do modyfikacji w ża-
den inny sposób).
Gdyby skojarzenie klasy Tournament z klasą PI ayer było jednokierunkowe, moglibyśmy
usunąć z klasy PI ayer kolekcję tournaments i wszystkie operujące na niej metody; zauważmy
jednak, że nic nie zmieniłoby się w interfejsie tej klasy i dla rozróżnienia kierunkowości skoja-
rzenia musielibyśmy zerknąć do jej implementacji.
Skojarzenia kwalifikowane. Jak pisaliśmy w rozdziale 2. „Modelowanie w języku UML",
kwalifikowanie skojarzeń „wiele na wiele" i „jeden na wiele" jest jedną z metod redukowania
ich krotności. Kwalifikator jest jednym z atrybutów klasy po stronie „wiele", o wartości uni-
kalnej w obrębie skojarzenia, choć niekoniecznie unikalnej globalnie. Jako przykład roz-
ważmy skojarzenie ligi (League) z graczami (Player) będącymi jej członkami (patrz rysu-
nek 10.10). Jest to typowe skojarzenie „wiele na wiele" — liga liczy wielu członków, a gracz
może być członkiem wielu lig. Załóżmy teraz, że gracz rejestrujący się w lidze wybiera sobie
unikalny nick (reprezentowany przez atrybut ni ckName klasy PI ayer), który będzie go iden-
tyfikował w ramach tej ligi (nick niekoniecznie unikalny w całym systemie ARENA i niekoniecznie
ten sam w różnych ligach).

Rysunek 10.10. Przykład realizacji dwukierunkowego skojarzenia kwalifikowanego; strzałki wskazują kie-
runek transformacji
464 Rozdział 10. • Odwzorowywanie modelu na kod

Jak łatwo zauważyć, skojarzenia kwalifikowane realizowane są odmiennie od „zwykłych"


skojarzeń typu „jeden na wiele" i „wiele na wiele". Najważniejsza z różnic kryje się w sposobie
implementowania kolekcji referencji po stronie kwalifikowanej — tym razem jest to mapa
(Map), a nie lista (Li st) czy zbiór (Set), a selektorem wyboru konkretnego obiektu (ze zbioru
obiektów skojarzonych) jest właśnie kwalifikator. Mapa ta utrzymywana jest w klasie League
jako atrybut pl ayers; znalezienie obiektu reprezentującego konkretnego gracza odbywa się
na podstawie jego nicku, będącego indeksem we wspomnianej mapie, przekazywanego jako
parametr wywołania metody g e t P l a y e r ( ) — uwalnia to programistę od czasochłonnego
iterowania po zbiorze referencji. Modyfikowanie wspomnianej mapy odbywa się poprzez wy-
wołania metod addPl ayer() i removePl ayer(). Po drugiej stronie skojarzenia wszystko po-
zostaje po staremu: kolekcja referencji do lig skojarzonych z graczem utrzymywana jest w po-
staci nieuporządkowanego zbioru, stanowiącego atrybut 1 eagues.

Klasy skojarzeniowe. W języku UML atrybuty i operacje związane ze skojarzeniem mogą


być reprezentowane w postaci klas skojarzeniowych. Przykładowo statystykę (Stati s t i cs)
osiągów konkretnego gracza (Pl ayer) w konkretnym turnieju (Tournament), czyli zestaw
danych dla każdej pary „turniej-gracz" można utrzymywać w postaci obiektów klasy skoja-
rzeniowej S t a t i s t i cs, po jednym obiekcie dla każdej takiej pary (patrz rysunek 10.11). By
koncepcję tę zrealizować, przekształcamy bezpośrednie skojarzenie „wiele na wiele" między
klasami Tournament i League na dwa skojarzenia typu „jeden na wiele" między klasą skoja-
rzeniową S t a t i s t i c s a każdą z dwu wymienionych klas. Każde z wynikowych skojarzeń kon-
wertujemy następnie na atrybuty referencyjne w sposób wcześniej opisany. Do realizacji klas
skojarzeniowych powrócimy jeszcze w sekcji 10.6.

Rysunek 10.11. Transformacja skojarzenia „wiele na wiele" na dwa skojarzenia z pośredniczącą kla-
są skojarzeniową
10.4. Aktywności odwzorowywania 465

Po zakończeniu odwzorowywania skojarzeń w pola i metody klas interfejsy tych klas


zostają względnie ustabilizowane i mogą zmieniać się jedynie wskutek uwzględniania nowych
wymagań, poprawiania wykrytych błędów lub refaktoryzowania kodu.

10.4.3. Odwzorowywanie kontraktów w wyjątki


Języki obiektowe realizujące koncepcję ograniczeń, takie jak Eiffel, umożliwiają automatyczne
weryfikowanie spełnienia kontraktów i generowanie wyjątków w przypadku ich naruszenia.
Użytkownicy klas mogą dzięki temu wykrywać błędy wynikające z przyjmowania niepraw-
dziwych (nieuzasadnionych) założeń dotyczących wykorzystywanej klasy, co staje się szczegól-
nie użyteczne przy wykrywaniu przypadków granicznych w zbiorze klas wykorzystywanych
do różnych celów. Generowanie wyjątków w sytuacji niespełnienia warunków końcowych
umożliwia implem entatorom wczesne wykrywanie błędów, z precyzyjną identyfikacją instrukcji
stanowiącej przyczynę konkretnego błędu.
Niestety, wiele języków zorientowanych obiektowo, między innymi Java, nie oferuje
wbudowanej obsługi kontraktów. Obsługę taką można jednak zaimplementować samodzielnie,
wykorzystując mechanizm wyjątków jako podstawę zarówno sygnalizowania sytuacji naru-
szenia kontraktu, jak i obsługiwania takich sytuacji. W języku Java wyjątki generuje się za po-
mocą słowa kluczowego throw, po którym następuje obiekt reprezentujący wyjątek; obiekt ten
jest pojemnikiem na szczegółową informację związaną z wyjątkiem, między innymi treść
komunikatu, obraz stosu wywołania w momencie wykonania instrukcji throw i tym podobne.
Wygenerowanie wyjątku polega na przerwaniu „normalnego" wykonywania kodu i przeka-
zaniu sterowania do „pasującej" instrukcji catch. Argumentem instrukcji catch jest wskazanie
obsługiwanej klasy wyjątku: jeżeli klasa ta jest tożsama z klasą zaistniałego wyjątku lub jest jej
superklasą, instrukcja catch „pasuje" do wyjątku — zostaje wtedy wykonany blok obsługi
wyjątku następujący po tej instrukcji.
Załóżmy, że w przykładzie pokazanym na listingu 10.3 metoda addPl a y e r ( ) k l a s y
TournamentControl wywoływana jest z parametrem reprezentującym gracza zarejestrowanego
już w tym turnieju. Metoda ta wygeneruje wówczas wyjątek klasy KnownPl ayer, który prze-
chwycony zostanie w celu obsługi przez metodę TournamentForm.addPl ayer() i przekazany
do klasy ErrorConsol e, po czym system przejdzie do obsługi następnego gracza. Obiekt brze-
gowy klasy ErrorConsol e wyświetli użytkownikowi stosowny komunikat.

Listing 10.3. Przykład generowania i obsługi wyjątku w języku Java. Klasa TournamentForm przechwytuje
wyjątek wygenerowany przez klasę TournamentControl i przekazuje go klasie ErrorConsol e w celu
wyświetlenia stosownego komunikatu dla użytkownika

public class TournamentControl {


private Tournament tournament;

public void addPlayer(PIayer p) throws KnownPlayerException {


if (tournament.isPlayerAccepted(p)) {
throw new KnownPlayerExcepti on(p);
}
... Normalna obsługa gracza ...
}
466 Rozdział 10. • Odwzorowywanie modelu na kod

}
public class TournamentForm {
private TournamentControl control;
private List players;

public void processPlayerApplications() {


// iteracja po wszystkich graczach, którzy zgłosili akces do turnieju

for (Iterator i = players.iterator(); i.hasNext();) {


try {
//Delegowanie wywołania do obiektu sterującego
control.acceptPlayer((Player)i.next());
} catch (KnownPlayerException e) {
// Po przechwyceniu wyjątku zarejestruj go na konsoli i przejdź
// do przetwarzania kolejnego gracza
ErrorConsole.log(e.getMessage());
}
}
}
}

W najprostszym wariancie kontrakty weryfikowane są oddzielnie dla każdej operacji.


W ciele metody realizującej operację umieszczane są nowe fragmenty kodu odpowiedzialne za
weryfikowanie spełnienia warunków wstępnych i końcowych oraz prawdziwości niezmienników.

• Weryfikowanie warunków wstępnych powinno odbywać się na początku metody,


zanim rozpocznie się jakiekolwiek związane z nią przetwarzanie. Poszczególne wa-
runki wstępne powinny być sprawdzane po kolei i gdy któryś z nich okaże się nie-
prawdziwy, generowany powinien być wyjątek; klasa wyjątku musi być inna dla
każdego warunku, tak by klient mógł na podstawie przechwyconego wyjątku ziden-
tyfikować błędny parametr. W ten sposób, jeżeli niespełnionych będzie kilka wa-
runków, sygnalizowany będzie tylko pierwszy przypadek ich naruszenia.
• Weryfikowanie warunków końcowych powinno następować na końcu metody, po
zakończeniu wszystkich czynności związanych ze zmianą stanu obiektu. Podobnie
jak w przypadku warunków wstępnych, poszczególne warunki końcowe sprawdzane
powinny być kolejno i w przypadku niespełnienia któregoś powinien być genero-
wany specyficzny wyjątek.
• Weryfikowanie niezmienników, w sytuacji gdy kontrakty dla każdej operacji rozwa-
żane są oddzielnie, odbywa się tak samo jak weryfikowanie warunków końcowych.
• Dziedziczenie wiąże się w tym przypadku także z dziedziczeniem przez subklasy nie-
których (lub wszystkich) fragmentów kodu dedykowanych weryfikowaniu kontraktów;
by dziedziczenie takie było możliwe, fragmenty te powinny zostać wyodrębnione
w postaci osobnych metod, wywoływanych zarówno z poziomu superklasy, jak
i z poziomu jej subklas.

Systematyczne zastosowanie powyższych reguł dla operacji Tournament.addPlayer()


prowadzi do kodu widocznego na rysunku 10.12.
10.4. Aktywności odwzorowywania 467

public class Tournament {

private List players;

public void addPlayer(Player p)


throws KnownPIayer, TooManyPlayers, UnknownPlayer,
111egalNumPlayers, 111egalMaxNumPlayers
{
// warunek wstępny precondition!isPlayerAccepted(p)
if (isPlayerAccepted(p)) {
throw new KnownPIayer(p);
}
// warunek wstępny getNumPlayersQ < maxNumPlayers
if (getNumPlayersQ == getMaxNumPlayers()) {
throw new TooManyPlayers(getNumPlayers());
}
II zapamiętanie wartości na użytek weryfikowania warunków końcowych
int pre_getNumPlayers = getNumPlayers();

U ******** Zasadniczy kod metody *********


players.add(p);
p.addTournament(thi s);
U ********

// warunek końcowy isPlayerAccepted(p)


if (!isPlayerAccepted(p)) {
throw new UnknownPlayer(p);
}
// warunek końcowy getNumPlayersQ = @pre.getNumPlayersQ + 1
if (getNumPlayers() != pre_getNumPlayers + 1) {
throw new II legal NumPl ayers (getNumPlayersQ);
}
// niezmiennik maxNumPlayers > 0
if (getMaxNumPlayers() <= 0) {
throw new II legalMaxNumPlayers(getMaxNumPlayers());
}
}
}
Rysunek 10.12. Kompletna implementacja metody Tournament .addPl a y e r ( ) wraz z weryfikacją jej
kontraktu
468 Rozdział 10. • Odwzorowywanie modelu na kod

Odwzorowując w powyższy sposób każdy kontrakt, upewniamy się co do tego, że przy


każdym wywołaniu dowolnej metody weryfikowane będzie spełnienie związanych z nią warun-
ków wstępnych, warunków końcowych i niezmienników. Perspektywa to atrakcyjna, bo prowa-
dząca do solidnego systemu — tyle że mało realna, chociażby ze względu na opisane niżej czynniki.

• Wysiłek programistów. W większości przypadków kod związany z weryfikacją kon-


traktu danej metody jest obszerniejszy i bardziej skomplikowany niż kod realizujący
„zasadniczą" funkcjonalność tej metody. Wyczerpująca weryfikacja kontraktów wy-
maga więc sporego wysiłku programistów, który lepiej byłoby spożytkować na po-
rządkowanie i testowanie kodu.
• Zwiększone ryzyko usterek. Każdy nowy kod to kod potencjalnie zawierający błędy,
dotyczy to także kodu, którego zadaniem jest właśnie wykrywanie błędów. Co gorsza,
jeśli „zasadniczy" kod metody i kod weryfikujący jej kontrakt tworzone będą przez
tego samego programistę, istnieje ryzyko zamaskowania błędu popełnionego w za-
sadniczym kodzie przez kod sprawdzający.
• Komplikacja kodu. Kod weryfikujący spełnienie ograniczeń jest z natury bardziej
skomplikowany niż same ograniczenia i komplikacja ta staje się uciążliwa, zwłaszcza
w sytuacji gdy wspomniane ograniczenia się zmieniają. Ryzyko wprowadzenia no-
wych błędów niweczy wówczas cały sens definiowania kontraktu.
• Degradacja wydajności. Systematyczne weryfikowanie wszystkich kontraktów może
znacząco — nawet o rząd wielkości — spowolnić wykonywanie programu. Mimo iż
poprawność programu jest zawsze wymaganiem najważniejszym, nie może jednak
usuwać w cień kwestii przepustowości i czasu reakcji.

Jeżeli zatem nie dysponujemy narzędziem w rodzaju iContract, opisanym przez R. Kra-
mera [Kramer, 1998], które dokonuje automatycznego generowania kodu, musimy zastoso-
wać podejście pragmatyczne, godzące względy poprawności systemu z wymienionymi powyżej
przesłankami przemawiającymi przeciwko nadmiernemu jej weryfikowaniu. Jak pamiętamy,
kontrakty stanowią narzędzie wspomagające komunikację między programistami, zatem ge-
nerowanie wyjątków w związku z naruszeniem tych kontraktów powinno koncentrować się
raczej wokół interfejsów klas niż wokół ich implementacji. Poniższy zestaw heurystyk może
okazać się pomocny przy rozstrzyganiu rozmaitych kompromisów w tym względzie.

Heurystyki pomocne w odwzorowywaniu naruszenia kontraktów w wyjątki


• Pomijaj sprawdzanie warunków końcowych i niezmienników. Kod weryfikujący te dwie kategorie
ograniczeń jest zazwyczaj redundantny z zasadniczym kodem metody i mało prawdopodobne jest, by
przyczynił się do odkrycia nowych błędów, chyba że pisany jest przez innego programistę lub testera.
• Koncentruj się na interfejsach podsystemów i unikaj weryfikowania kodu dotyczącego metod
prywatnych i chronionych. To właśnie granice podsystemów wyznaczają granice odpowiedzialności
między poszczególnymi programistami i z natury zmieniają się rzadziej niż wewnętrzne interfejsy klas.
• Faworyzuj k o m p o n e n t y o dłuższym czasie życia, czyli te, których kod jest wykorzystywany
w największym stopniu; dotyczy to przeważnie obiektów encji, rzadko obiektów brzegowych.
• Wykorzystuj wielokrotnie ten sam kod weryfikujący ograniczenia: warunki wstępne dla wielu
operacji są bardzo podobne, a niekiedy identyczne, warto zatem zamykać ich weryfikację w formę
metod współdzielących te same klasy wyjątków.
10.4. Aktywności odwzorowywania 469

Kod dedykowany sprawdzaniu ograniczeń powinien być udokumentowany za pomocą


odpowiednich komentarzy, opisujących przedmiotowe ograniczenia zarówno w języku natu-
ralnym, jak i w postaci formalnej, na przykład w języku OCL. Gdy ograniczenia się zmienią,
łatwo będzie można zrewidować kod programu pod kątem konsekwencji tej zmiany.

10.4.4. Odwzorowywanie modelu obiektowego


w schematy bazy danych
Dotąd nie wyróżnialiśmy obiektów trwałych w żaden szczególny sposób. Rozróżnienie takie
jest jednak konieczne z tego względu, że większość języków programowania nie posiada wbu-
dowanych mechanizmów przechowywania tych obiektów, w związku z czym zmuszeni jeste-
śmy we własnym zakresie dokonywać ich odwzorowywania w struktury danych nadające się
do magazynowania w dostępny sposób, czyli przede wszystkim w formie relacyjnych baz da-
nych i w formie niezależnych plików. W przypadku obiektowych baz danych takie odwzoro-
wywanie byłoby niepotrzebne, jednak „płaskie" pliki i relacyjne bazy danych wymagają kon-
wersji obiektów do i z postaci, w jakiej są w ramach tych mediów reprezentowane. W tej
sekcji przeanalizujemy odwzorowywanie modelu obiektowego w system relacyjnej bazy da-
nych z wykorzystaniem języka Java i schematów bazodanowych.
Wspomniane schematy to opisy danych — każdy schemat jest metamodelem danych,
o czym pisze C. J. Date [Date, 2004]. W języku UML za pomocą diagramów klas opisujemy
klasy w kategoriach struktury obiektów, które tworzone będą w kodzie źródłowym; na po-
dobnej zasadzie schemat bazy danych opisuje strukturę rekordów, które mogą być w tej bazie
przechowywane. Relacyjna baza danych stanowi więc magazyn, w którym dane przechowy-
wane są wraz z opisującym ich strukturę schematem.
Dane w bazie relacyjnej zorganizowane są w strukturę tabel (często zwanych w literaturze
relacjami). Każda z tych tabel może być utożsamiana z macierzą, której kolumny reprezentują
poszczególne atrybuty obiektu — w przykładzie widocznym na rysunku 10.13 są to trzy atry-
buty obiektu User: f i rstName, 1 ogi n i emai 1. Wiersze wspomnianej macierzy odpowiadają
poszczególnym rekordom tabeli, reprezentującym konkretne obiekty: w tabeli z rysunku 10.13
są to obiekty reprezentujące użytkowników al i ce, john i bob.

Rysunek 10.13. Przykład tabeli relacyjnej bazy danych, z trzema kolumnami i trzema rekordami
470 Rozdział 10. • Odwzorowywanie modelu na kod

Kluczem głównym (primary key) tabeli jest minimalny zbiór jej atrybutów ze swej natury
jednoznacznie identyfikujący poszczególne jej rekordy. Klucz ten pełni istotną rolę w identy-
fikowaniu rekordów tabeli w przypadku ich modyfikowania, usuwania i dodawania nowych.
W tabeli z rysunku 10.13 rolę klucza głównego może pełnić atrybut 1 ogi n, lecz równie do-
brze mógłby ją pełnić atrybut emai 12. Każdy zbiór atrybutów kwalifikujących się do miana
klucza głównego nazywany jest kluczem kandydackim; klucz główny jest więc rezultatem
wyboru spośród wszystkich kluczy kandydackich.
Klucz obcy (foreign key) to zbiór atrybutów (lub pojedynczy atrybut) stanowiący odwoła-
nie do klucza głównego innej tabeli. Klucz obcy wiąże konkretny rekord jednej tabeli z rekor-
dem lub grupą rekordów w innej. Na rysunku 10.14 widzimy fragment tabeli League ukazujący
dwie kolumny reprezentujące nazwę ligi (name) i login jej kapitana (owner) — ten ostatni
jest kluczem obcym do tabeli User z rysunku 10.13. Tak więc Alice (login am384) jest kapitanem
lig t i ctactoeNovi ce i t i ctactoeExpert, zaś John (login j s289) jest kapitanem ligi chessNovi ce.

Rysunek 10.14. Przykład klucza obcego: atrybut owner tabeli League odpowiada kluczowi główne-
m u tabeli User

Odwzorowywanie klas i atrybutów

Odwzorowując trwałe dane na schematy relacyjnych baz danych, koncentrujemy się


w pierwszej kolejności na klasach i ich atrybutach: każdą klasę odwzorowujemy w tabelę o toż-
samej z nią nazwie, zaś dla każdego atrybutu tej klasy tworzymy w tej tabeli kolumnę (o nazwie
tożsamej z nazwą tegoż atrybutu). Każdy rekord tej tabeli odpowiadać będzie konkretnej in-
stancji (obiektowi) wspomnianej klasy. Utrzymując spójność między nazwami modelu i na-
zwami schematu bazy danych, zapewniamy identyfikowalność między obiema reprezentacjami,
tak istotną w przypadku wprowadzania zmian do modelu.
Odwzorowując atrybuty klasy w kolumny schematu tabeli, stajemy przed zadaniem okre-
ślenia typu wartości reprezentowanych przez poszczególne kolumny. W przypadku gdy typ
atrybutu związanego z daną kolumną jest typem elementarnym, jest to zadanie banalne (przy-
kładowo typ Date języka Java odwzorowywany jest w typ datetime języka SQL). W przypadku
typów „mniej oczywistych" sprawa trochę się komplikuje, na przykład odwzorowywanie typu

2
Pod warunkiem, że dwoje różnych użytkowników nie będzie mogło współdzielić tego samego ad-
resu e-mail, co nie jest takie oczywiste jak w przypadku unikalności loginu — przyp. tłum.
10.4. Aktywności odwzorowywania 471

Stri ng w pole tekstowe jest o tyle utrudnione, że większość systemów baz danych (zgodnie
zresztą z semantyką języka SQL) wymaga określenia maksymalnej długości przechowywanego
napisu. I tak na przykład w systemie ARENA, odwzorowując klasę User, można ograniczyć dłu-
gość nazwy (imienia) użytkownika do 25 znaków, co odpowiada typowi text [25] języka SQL.
Ograniczenie to wiąże się z koniecznością zdefiniowania odpowiednich warunków wstępnych
dla obiektów encji i obiektów brzegowych.
Klucz główny tabeli zdefiniować można na dwa sposoby: identyfikując zbiór atrybutów
unikalnie identyfikujący każdy rekord 3 albo definiując w tym celu osobny atrybut, którego
unikalna wartość będzie automatycznie generowana dla każdego nowo dodawanego rekordu.
W tabeli pokazanej na rysunku 10.13 zastosowaliśmy pierwsze podejście. Mimo iż jest
to podejście intuicyjne, ma jednak tę niedogodność, że gdy zmieni się wartość pola wchodzą-
cego w skład klucza (tu: pola 1 ogi n), należy zaktualizować wszystkie tabele, które powołują się
na ten klucz (za pomocą swego klucza obcego). Jeżeli ponadto atrybuty wchodzące w skład
klucza głównego zaczerpnięte są z dziedziny aplikacyjnej, stajemy przed perspektywą ewentu-
alnej przebudowy całej bazy w przypadku zmian zachodzących w tej dziedzinie. Przykładowo
w systemie ARENA login każdego użytkownika jest unikalny w ramach jednej instancji tego
systemu. Jeżeli w przyszłości zdecydowalibyśmy się utrzymywać w pojedynczej tabeli dane
wszystkich użytkowników powiązanych z pewnym zbiorem instancji tego systemu, może się
zdarzyć, że w ramach tej tabeli dwóch użytkowników posiadać będzie ten sam login — nie bę-
dziemy więc mogli użyć pola 1 ogi n w charakterze klucza głównego.
Generowanie unikalnego, niezmiennego identyfikatora (id) dla każdego nowo doda-
wanego rekordu tabeli oferowane jest przez wiele współczesnych systemów baz danych. Daje
ono w rezultacie bardziej solidny schemat i prostszy, bo jednokolumnowy, klucz. Przykładowo
w systemie ARENA klasę User odwzorować można w tabelę o czterech kolumnach: i d, f i rstName,
1 ogi n i emai 1 (patrz rysunek 10.15). W pole i d każdego nowo tworzonego rekordu wpisywana
jest wartość pewnej ukrytej zmiennej, inkrementowanej przy każdym dodawaniu rekordu.

Rysunek 10.15. Odwzorowanie klasy User w tabelę bazy danych, z wykorzystaniem automatycznie
generowanego pola kluczowego (i d)

3
Tak zdefiniowany klucz główny nazywany jest kluczem naturalnym — przyp. tłum.
472 Rozdział 10. • Odwzorowywanie modelu na kod

Odwzorowywanie skojarzeń

Po odwzorowaniu poszczególnych klas w tabele relacyjnej bazy danych przychodzi czas


na odwzorowanie skojarzeń między klasami. Postać tego odwzorowania zależna jest od krot-
ności wspomnianych skojarzeń: odwzorowania skojarzeń „jeden do jednego" i „jeden na wiele"
implementowane są zwykle za pomocą tak zwanych skrytych powiązań, opisanych przez
M. Blahę i W. Premerlaniego [Błaha i Premerlani, 1998], opartych na kluczach obcych, podczas
gdy skojarzenia typu „wiele na wiele" implementowane są z użyciem tabel pośredniczących.
Skryte powiązania (buried association). Skojarzenia o krotności „jeden" na jednym lub
obu końcach mogą być implementowane przy wykorzystywaniu kluczy obcych. Dla skojarzeń
„jeden na wiele" klucz obcy definiowany jest w tabeli reprezentującej klasę po stronie „wiele"
skojarzenia, dla skojarzenia „jeden do jednego" może być definiowany w dowolnej z dwóch
powiązanych tabel. Na rysunku 10.16 widoczne są dwie skojarzone tabele: LeagueOwner, repre-
zentująca klasę kapitanów lig, oraz League, reprezentująca same ligi. Można być kapitanem
wielu lig jednocześnie, dlatego skojarzenie jest typu „jeden na wiele". W tabeli League definiujemy
więc klucz obcy oparty na polu owner, odwołujący się do klucza głównego tabeli LeagueOwner.
Jest zrozumiałe, że kilka rekordów tabeli Leauge może mieć w polu owner tę samą wartość.
W przypadku skojarzeń dopuszczających krotność zero po jednej ze stron, może się zdarzyć,
że pole wchodzące w skład klucza obcego zawierać będzie pustą wartość (nuli), oznaczającą
brak skojarzonego rekordu w powiązanej tabeli.

Rysunek 10.16. Przykład skrytego powiązania implementującego skojarzenie „jeden na wiele"

Tabela pośrednicząca. Skojarzenia „wiele na wiele" implementowane są za pomocą


oddzielnej, dwukolumnowej tabeli, powiązanej za pomocą kluczy obcych z tabelami repre-
zentującymi klasy uczestniczące w skojarzeniu; tabelę tę nazywa się (ze względu na pełnioną
funkcję) tabelą skojarzeniową. Każdy wiersz (rekord) tabeli skojarzeniowej reprezentuje po-
wiązanie między dwiema instancjami (obiektami) skojarzonych klas. W systemie ARENA skoja-
rzenie „wiele na wiele" występuje między klasami (Tournament i Pl ayer). „Lewa" kolumna ta-
beli skojarzeniowej jest więc kluczem obcym do tabeli Tournament, prawa zaś — do tabeli
Pl ayer (obie tabele — Tournament i Pl ayer — mają klucz główny w postaci automatycznie
generowanego identyfikatora id). Dla każdego turnieju (Tournament) istnieje w tabeli
skojarzeniowej (TournamentPl ayerAssoci at i on) zbiór rekordów prowadzących do graczy
zarejestrowanych w tym turnieju, analogicznie dla każdego gracza (Pl ayer) istnieje w tej tabeli
10.4. Aktywności odwzorowywania 473

zbiór rekordów prowadzących do turniejów, w których gracz ten się zarejestrował. Z zawartości
tabel przedstawionych na rysunku 10.17 wynika, że gracze al ice i john zarejestrowani są
w t u r n i e j u novi ce.

Rysunek 10.17. Implementacja przykładowego skojarzenia „wiele na wiele" za pomocą pośredniczą-


cej tabeli skojarzeniowej

Zauważmy, że skojarzenia „jeden do jednego" i „jeden na wiele" również mogą być re-
alizowane za pomocą tabeli skojarzeniowej, zamiast przy użyciu skrytych powiązań. Imple-
mentacja taka sprawia, że schemat staje się bardziej elastyczny: jeżeli na przykład zmienimy
typ odnośnego powiązania z „jeden na wiele" na „wiele na wiele", nie będzie trzeba w ogóle
zmieniać schematu bazy danych. Ceną płaconą za tę elastyczność będzie jednak większa liczba
tabel i bardziej czasochłonna trawersacja skojarzeń — znowu mamy do czynienia z kompro-
misem, który rozstrzygnięty musi być w oparciu o takie przesłanki jak prawdopodobieństwo
zmiany typu skojarzenia w przyszłości oraz krytyczność czasu reakcji systemu.

Odwzorowywanie relacji dziedziczenia

Relacyjne bazy danych nie zapewniają bezpośredniej obsługi dziedziczenia, dlatego


musi ono być odwzorowywane w schematy baz, analogicznie do skojarzeń. Odwzorowanie
to można przeprowadzić na dwa sposoby. Pierwszy z nich, zwany odwzorowaniem pionowym,
podobnie jak skojarzenie „jeden do jednego", opiera się na wykorzystaniu klucza obcego
prowadzącego od tabeli reprezentującej subklasę do tabeli reprezentującej superklasę. W ra-
mach drugiej opcji, zwanej odwzorowaniem poziomym, atrybuty superklasy dublowane są
w tabeli reprezentującej subklasę.

Odwzorowanie pionowe. Każda z klas — superklasa i subklasa — odwzorowane zostają w po-


staci odrębnych tabel. Tabela reprezentująca subklasę nie jest samowystarczalna — informacja
na temat danego obiektu subklasy reprezentowana jest przez parę skojarzonych rekordów w obu
tabelach. Tabela reprezentująca superklasę zawiera kolumny odpowiadające wszystkim jej atry-
butom, a ponadto kolumnę identyfikującą konkretną subklasę obiektu reprezentowanego przez
dany rekord; kolumna ta jednocześnie identyfikuje tabelę zawierającą rekord reprezentujący po-
zostałą informację z danego obiektu. W tabeli reprezentującej subklasę nie ma kolumn odpowia-
dających atrybutom dziedziczonym z superklasy. Obie tabele współdzielą ten sam klucz główny.
474 Rozdział 10. • Odwzorowywanie modelu na kod

Zasadzę odwzorowania pionowego pokazano na rysunku 10.18 na przykładzie klas


systemu ARENA. Odwzorowaniu podlega tu superklasa User oraz jej dwie subklasy: LeagueOwner
i Pl ayer. Tabela User zawiera kolumnę reprezentującą atrybut name oraz kolumnę roi e, iden-
tyfikującą subklasę obiektu reprezentowanego przez rekord. Wszystkie trzy tabele współ-
dzielą ten sam klucz główny, oparty na kolumnie i d; dwa rekordy, z których pierwszy pochodzi
z tabeli User, drugi zaś z tabeli LeagueOwner lub Pl ayer — jeżeli mają ten sam klucz główny,
składają się na reprezentację danego obiektu. I tak na przykład klucz „56" identyfikuje rekord
składający się na reprezentację obiektu klasy LeagueOwner (o czym informuje zawartość pola
roi e tego rekordu w tabeli User), zaś w tabeli LeagueOwner rekord o tym kluczu ma w polu
maxNumLeagues wartość „12". A zatem rekord o kluczu głównym 56 reprezentuje obiekt klasy
LeagueOwner, którego atrybut name ma wartość „zoe", zaś atrybut maxNumLeagues — wartość
„12". Na podobnej zasadzie klucz „79" identyfikuje obiekt klasy Player, o wartościach atry-
butów name i c r e d i t s równych (odpowiednio) „john" i „126".

Rysunek 10.18. Przykład realizacji dziedziczenia klas w postaci odwzorowania pionowego

Gdy więc chcemy odczytać z bazy określony obiekt, rozpoczynamy od tabeli reprezentu-
jącej superklasę. Poszczególne pola odnalezionego rekordu zawierać będą informację dziedzi-
czoną z superklasy oraz wskazanie tabeli reprezentującej subklasę, w której to tabeli należy poszu-
kiwać rekordu zawierającego resztę informacji. W przypadku dziedziczenia wielopoziomowego
opisaną zależność stosuje się rekurencyjnie.
10.5. Zarządzanie transformacjami 475

Odwzorowanie poziome. Inny sposób realizacji dziedziczenia polega na reprezentowaniu


każdej z subklas przez pojedynczą, kompletną tabelę, w efekcie czego staje się zbędna tabela re-
prezentująca superklasę4. W każdej z tabel reprezentujących subklasy powielane są kolumny
związane z atrybutami dziedziczonymi z superklasy. W przypadku klas LeagueOwner i PI ayer
wiąże się to z obecnością atrybutu name w każdej z nich — na rysunku 10.19 przedstawiony jest
„poziomy" wariant realizacji dziedziczenia z rysunku 10.18. W celu odczytania z bazy żądanego
rekordu konieczne jest teraz przeszukiwanie tylko jednej tabeli.

Rysunek 10.19. Przykład realizacji dziedziczenia klas w postaci odwzorowania poziomego

Wybór między odwzorowaniem pionowym a poziomym jest wyborem między elastycz-


nością systemu a jego reaktywnością. W odwzorowaniu pionowym dodanie nowego atrybutu
trwałego do superklasy wymaga zmodyfikowania schematu tylko jednej tabeli — reprezentu-
jącej superklasę; w odwzorowaniu poziomym wymaga to zmodyfikowania schematów wszyst-
kich tabel reprezentujących subklasy, a to jest już bardziej złożone i bardziej podatne na błędy.
Z kolei w przypadku odwzorowania poziomego komplet informacji o obiekcie zawarty jest
w pojedynczym rekordzie, podczas gdy w mapowaniu pionowym informacja ta rozproszona
jest między dwie lub więcej tabel, co przekłada się na dłuższy czas wyszukiwania obiektów
i mniejszą reaktywność systemu, zwłaszcza przy dziedziczeniach wielopoziomowych. Osta-
tecznie należy więc zastanowić się, czy prawdopodobieństwo zmian modelu jest na tyle duże,
iż warto poświęcić dlań pewien stopień wydajności, stosując odwzorowanie pionowe.

10.5. Zarządzanie transformacjami

10.5.1. Dokumentowanie transformacji


Transformacje umożliwiają usprawnianie wybranych aspektów modelu obiektowego i jego
transformowanie na kod źródłowy. Wypracowując systematyczne recepty dla powtarzających
się sytuacji, oszczędzamy sobie sporo wysiłku i zmniejszamy ryzyko wprowadzania błędów do

4
O ile nie będą w bazie przechowywane rekordy będące instancjami tej superklasy — przyp. tłum.
476 Rozdział 10. • Odwzorowywanie modelu na kod

kodu źródłowego. By jednak tego typu doświadczenia mogły zachowywać swą użyteczność
przez cały czas życia systemu, konieczne jest dokumentowanie wszelkich transformacji, tak
by można było je w sposób spójny powtarzać w przypadku wprowadzania zmian do modelu
lub kodu źródłowego.
Inżynieria odwracająca pomyślana została jako działanie mające złagodzić związane
z tym problemy poprzez odtworzenie modelu obiektów na podstawie kodu źródłowego. Ge-
neralnie, jeżeli utrzymujemy odpowiedniość „jeden do jednego" między kodem źródłowym
a modelem, nie potrzebujemy żadnej dokumentacji: używane narzędzia powinny zapewnić
spójność kodu z modelem, gdy wprowadzamy zmiany do jednego lub drugiego. Problem jed-
nak w tym, że te najbardziej użyteczne transformacje, włącznie z opisywanymi w tym rozdziale,
nie są transformacjami „jeden do jednego" i gubią część informacji.

• Skojarzenia jednokierunkowe typu „jeden na wiele" dają w rezultacie taki sam kod
źródłowy jak skojarzenia jednokierunkowe typu „wiele na wiele" w procesie trans-
formowania w kolekcje. Narzędzia inżynierii odwracającej przyjmują w takiej sytuacji
wariant mniej restrykcyjny, czyli zakładają typ „wiele na wiele" oryginalnego skoja-
rzenia. Ogólnie informacja na temat krotności danego skojarzenia rozproszona jest
po kodzie źródłowym, między innymi znajduje się zwykle również w kodzie weryfi-
kującym kontrakty dla obiektów brzegowych.
• Skryte powiązania implementujące w schematach baz danych skojarzenia typu
„jeden do jednego" i „jeden na wiele" obarczone są tym samym mankamentem. Co
więcej, implementacje skojarzeń za pomocą tabel pośredniczących kompletnie gubią
informację na temat typu tych skojarzeń.
• Warunki końcowe i niezmienniki. Odwzorowując niespełnione kontrakty w genero-
wanie wyjątków (sekcja 10.4.3), uwzględniamy zwykle tylko warunki wstępne, pomi-
jając sprawdzanie warunków końcowych niezmienników. Ryzykujemy w ten sposób
gubienie informacji o warunkach końcowych i niezmiennikach, przez co w wyniku
kolejnych zmian system może stawać się coraz bardziej niespójny.

Powyższe okoliczności zmuszają wręcz do opracowania konwencji i mechanizmów


umożliwiających zachowanie wzajemnej spójności modelu projektu obiektów, kodu źródło-
wego i dokumentacji. Trudno podać w tym względzie jakąś jedną uniwersalną receptę, stano-
wiącą panaceum na wszelkie problemy, można jednak pokusić się o kilka ogólnych wskazó-
wek. Oto one.

• Należy do danej transformacji używać konsekwentnie tego samego narzędzia. Gdy


wykorzystujemy określone narzędzie do odwzorowania skojarzeń w kod źródłowy,
należy użyć go ponownie, gdy zmienią się typy lub krotności skojarzeń. Nowoczesne
narzędzia do modelowania wstawiają do generowanego kodu odpowiednie znaczniki,
zapewniające spójność przy wielokrotnym generowaniu kodu na bazie tego samego
modelu. Spójność tę można jednak łatwo zniszczyć, operując na kodzie lub modelu
przy użyciu także innych narzędzi, na przykład edytorów tekstu. Podobnie, kiedy
stosujemy określone narzędzie do generowania kodu weryfikującego ograniczenia
wynikające z kontraktu, należy ponownie użyć tego samego narzędzia w przypadku
zmiany tych ograniczeń.
10.5. Zarządzanie transformacjami 477

• Zapisy ograniczeń należy utrzymywać w kodzie źródłowym, a nie w modelu. Kon-


trakty wyznaczają zasady zachowania się metod i formułują ograniczenia narzucone
na parametry oraz atrybuty. Programiści zmieniają zachowanie obiektów, mody-
fikując ciała metod, a nie model obiektowy. Gdy zapisy ograniczeń utrzymywane są
w postaci komentarzy wplatanych w kod źródłowy, wygodniej je modyfikować przy
wprowadzaniu zmian do tego kodu.
• Należy używać tej samej nazwy dla wszystkich inkarnacji danego obiektu. Odwzoro-
wując skojarzenie na kod źródłowy lub klasę na schemat bazy danych, należy używać
tej samej nazwy na obu krańcach odwzorowania. Jeśli jakaś nazwa zmienia się w mo-
delu, musi się odpowiednio zmienić także w kodzie źródłowym. Zapewniamy sobie
w ten sposób identyfikowalność modelu i ułatwiamy programistom rozpoznawanie
odpowiadających sobie elementów po obu stronach transformacji. Zasada ta stanowi
jednocześnie silny argument na rzecz wyboru odpowiednich nazw na etapie analizy
wymagań, zanim model poddany zostanie jakiejkolwiek transformacji. Późniejsze
zmiany nazewnictwa są zwykle bardzo kłopotliwe i wysoce podatne na błędy.
• Reguły przeprowadzania transformacji powinny być oczywiste, by wszyscy programi-
ści mogli dokonywać transformacji w taki sam sposób. Przykładowo szczegóły od-
wzorowywania skojarzeń na kolekcje powinny być udokumentowane w formie prze-
wodnika zawierającego reguły i konwencje, tak by dwóch (dwoje) programistów,
przeprowadzając tę samą transformację na bazie tego samego modelu, otrzymało do-
kładnie taki sam kod źródłowy. Spójność w tym względzie staje się istotna z per-
spektywy identyfikowania poszczególnych elementów modelu we wspomnianym
kodzie. Krytycznym czynnikiem staje się tutaj nie tyle konkretna postać samych
konwencji, ile ich konsekwentne przestrzeganie przez wszystkich programistów.

10.5.2. Przydzielanie odpowiedzialności


Dla wyboru, przeprowadzania i dokumentowania transformacji oraz konwersji modelu na kod
źródłowy konieczne jest współdziałanie kilku ról.

• Główny architekt dokonuje wyboru transformacji do systematycznego stosowania;


jeśli przykładowo schemat bazy danych ma być przede wszystkim modyfikowalny,
główny architekt może zdecydować o implementowaniu wszystkich skojarzeń za po-
mocą tabel pośredniczących.
• Łącznik architektoniczny odpowiedzialny jest za dokumentowanie kontraktów
związanych z interfejsami podsystemów. Gdy jakiś kontrakt któregoś z interfejsów
ulegnie zmianie, łącznik architektoniczny odpowiedzialny jest za powiadomienie
o tym fakcie wszystkich programistów uzależnionych od tego interfejsu.
• Programista odpowiedzialny jest za fizyczne wykonywanie transformacji i konwersji
modelu na kod źródłowy w zgodzie z konwencjami określonymi przez głównego ar-
chitekta. Programiści odpowiedzialni są także za utrzymywanie aktualności ko-
mentarzy wplatanych w kod źródłowy.
478 Rozdział 10. • Odwzorowywanie modelu na kod

Przeprowadzenie danej transformacji po raz pierwszy nie wydaje się być zadaniem szcze-
gólnie skomplikowanym, ta oczywistość blednie jednak przy powtórnym stosowaniu wspo-
mnianej transformacji w wyniku wprowadzonych zmian. Dlatego przypisując poszczególne
zakresy odpowiedzialności, należy być świadomym tego, kto powinien być powiadamiany o tych
zmianach.

10.6. Analiza przypadku — system ARENA


Pokażemy teraz, na obszernym przykładzie, zastosowanie opisanych w tym rozdziale koncepcji
i metod do systemu ARENA. Skoncentrujemy się na klasach otaczających klasę S t a t i s t i cs,
odpowiedzialną za śledzenie statystyki związanej z daną grą, graczem, turniejem i ligą. Pod-
stawowym problemem jest tu realizacja skojarzeń między poszczególnymi klasami w sposób
zapewniający zarówno zbieranie rozmaitych statystyk, jak i otwartość na definiowanie no-
wych w przyszłości. Mechanizm statystyk musi być też przezroczysty na rozbudowę systemu,
między innymi dodawanie nowych gier (Game) i stylów rozgrywek (TorunamenStyle).
Rozpoczniemy od szczegółowego opisania samej klasy Stati s t i c s . Następnie odwzo-
rujemy w postaci różnych kolekcji skojarzenia obiektów klasy Stati s t i cs z innymi obiektami,
po czym zajmiemy się weryfikowaniem kontraktów związanych z klasą S t a t i s t i c s . Na za-
kończenie zaprojektujemy schemat bazy danych dla przechowywanych w tej bazie klas i sko-
jarzeń między nimi.

10.6.1. Statystyki systemu ARENA


Obiekt klasy Stati s t i cs odpowiedzialny jest za utrzymywanie szeregu liczników związanych
z różnymi zakresami zbierania informacji ilościowych w kontekście określonej gry (Game).
Przykładowo dla kibica (Spectator) powinny być dostępne statystyki poszczególnych graczy
w poszczególnych turniejach (na przykład średnia liczba ruchów wykonywanych w jednym
meczu przez Johna w zimowym turnieju gry w kółko i krzyżyk), a także statystyki związane
z członkami poszczególnych lig (na przykład średnia liczba ruchów przypadających na jeden
mecz Johna w lidze nowicjuszy gry w kółko i krzyżyk). Kibic powinien mieć także możliwość
oglądania statystyk bardziej ogólnych, na przykład dotyczących ogółu graczy z danej ligi.
W rezultacie system ARENA powinien być zdolny do zbierania statystyk obejmujących nastę-
pujące zakresy (w kolejności od najbardziej ogólnego do najbardziej szczegółowego):

• wszystkie mecze związane z daną grą,


• wszystkie mecze rozgrywane przez członków danej ligi,
• wszystkie mecze danej ligi rozgrywane przez konkretnego gracza,
• wszystkie mecze w ramach danego turnieju,
• wszystkie mecze w ramach danego turnieju rozgrywane przez konkretnego gracza.

W sekcji 8.6.1 pokazaliśmy, jak za pomocą wzorca projektowego Fabryka abstrakcyjna


można zrealizować niezależność systemu ARENA od specyfiki konkretnych gier: jednym ze
specyficznych obiektów zwracanych przez tę fabrykę jest obiekt będący specjalizacją obiektu
S t a t i s t i cs, przystosowaną do konkretnej gry. Implementacja tej specjalizacji, w zgodzie
10.6. Analiza przypadku — system ARENA 479

z interfejsem klasy S t a t i s t i cs, jest zadaniem programistów opracowujących grę. Jeżeli kon-
kretna gra nie wymaga specyficznej statystyki, system ARENA oferuje jej programistom klasę
Defaul t S t a t i s t i c s realizującą statystykę standardową. Jak wynika z rysunku 10.20, system
ARENA nie odwołuje się do konkretnych specjalizacji klas Game i Stati s t i c s , co jest potwier-
dzeniem jego niezależności od specyfiki konkretnej gry.

Rysunek 10.20. Specjalizowany obiekt Stat istics jako jeden z produktów Fabryki abstrakcyjnej Game

Jednym z celów projektowych systemu ARENA jest nadanie interfejsowi klasy Stati s t i cs
jak najprostszej postaci, tak by programiści tworzący nowe gry mogli z łatwością imple-
mentować zbieranie statystyki w sposób specyficzny dla tej gry; innymi słowy, specjalizacja
obiektu Stati s t i cs dla konkretnej gry powinna być odpowiedzialna jedynie za definiowanie
formuł statystycznych dla tej gry — zarządzanie zakresami poszczególnych statystyk leży już
bowiem w gestii systemu ARENA.
Wobec powyższego na etapie projektowania obiektów podejmujemy decyzję o obli-
czaniu wszelkich statystyk w sposób przyrostowy, to znaczy po zakończeniu każdego meczu
(Match). Dla każdego zakresu przewidzianego w systemie ARENA (vide lista na początku tej
sekcji) utrzymywać więc będziemy oddzielny licznik, co prowadzi do osobnych obiektów
S t a t i s t i cs dla każdej gry (Game), ligi (League) i turnieju (Tournament) oraz dla każdej kom-
binacji „gracz-gra", „gracz-liga" i „gracz-turniej".
I tak na przykład załóżmy, że gracz John bierze udział w dwóch turniejach: t l i tZ, w lidze
nowicjuszy gry w kółko i krzyżyk. Po pewnym czasie awansuje do ligi ekspertów tej gry i bie-
rze udział w turnieju t 3 organizowanym przez tę ligę. Załóżmy ponadto, że interesują nas
liczba wygranych Johna w każdej z gier (Game). Spełnienie powyższych założeń wymaga zbie-
rania sześciu statystyk, obejmujących:
480 Rozdział 10. • Odwzorowywanie modelu na kod

• zwycięstwa Johna w każdym z turniejów 11, t2 i t3 (3 statystyki),


• zwycięstwa Johna w turniejach organizowanych przez poszczególne ligi (2 statystyki),
• zwycięstwa Johna w grze w kółko i krzyżyk (1 statystyka).

W języku UML ów skomplikowany zbiór interakcji przedstawić można za pomocą


n-arnego skojarzenia klasy S t a t i s t i c s z klasami Player, Game, League i Tournament
(patrz rysunek 10.21). W konkretne skojarzenie uwikłana jest, oczywiście, tylko jedna z klas
Game, League albo Tournament, co odpowiada zbieraniu statystyki dla (kolejno) konkretnej gry,
konkretnej ligi i konkretnego turnieju.

Rysunek 10.21. N-arne skojarzenie klasy S t a t i s t i c z klasami PI ayer, Game, League i Tournament

10.6.2. Odwzorowywanie skojarzeń na kolekcje


N-arne skojarzenie S t a t i s t i c s odwzorujemy w klasę Stati s t i cs języka Java; jedynym zada-
niem tej klasy będzie utrzymywanie wartości poszczególnych liczników. Klasie tej towarzyszyć
będzie obiekt singleton klasy S t a t i s t i csVaul t, noszący nazwę Simpl e S t a t i s t i csVaul t;
jego zadaniem będzie śledzenie powiązań między obiektami S t a t i s t i cs a obiektami Game,
League, Tournament i Player (patrz rysunek 10.22). Wyposażymy obiekt S t a t i s t i csVaul t
w operacje udostępniające statystyki z żądanych zakresów. Jeśli żądany obiekt S t a t i s t i c s nie
będzie istniał, obiekt Simpl eStati s t i csVaul t utworzy go, wywołując metodę Game, create
^Statistics (). Wewnętrznie obiekt Simpl eStati s t i csVaul t implementowany będzie z uży-
ciem mapy (HashMap) przechowującej powiązania między obiektami PI ayer, Game, League
i Tournament (patrz rysunek 10.22).
Widoczny na tym rysunku obiekt Simpl eStati s t i csVaul t nie wykonuje żadnych in-
nych zadań oprócz zarządzania stanem wspomnianych powiązań. Wartości poszczególnych
statystyk uaktualniane są po zakończeniu każdego meczu (Match) przez obiekt Torunament-
Control, który w tym celu najpierw kilkakrotnie wywołuje metodę g e t S t a t i s t i c s O b j e c t ( )
obiektu Simpl eStati s t i csVaul t zwracającą żądane obiekty Stati s t i cs, po czym dla każdego
z tych obiektów wywołuje metodę update(). Podobnie obiekt brzegowy S t a t i s t i csVi ew,
wyświetlający użytkownikowi żądaną wartość statystyki, najpierw za pomocą obiektu
Simpl eStati s t i csVaul t uzyskuje odpowiedni obiekt S t a t i s t i c s , po czym wywołuje jego
metodę g e t S t a t Q . Zauważmy, że w obu przypadkach procedura jest dwukrokowa: wywoły-
wane są dwie metody, związane (kolejno) z uzyskaniem odpowiedniego obiektu Stati s t i c s
i pobraniem z niego wartości żądanej statystyki.
10.6. Analiza przypadku — system ARENA 481

Rysunek 10.22. Realizacja n-arnego skojarzenia za pomocą obiektu singletonu

Byłoby idealnie, gdybyśmy doprowadzili do sytuacji, w której obiekty TournamentControl


i Stati s t i csVi ew wywołują tylko jedną metodę, uprościłoby to bowiem generalnie zarządzanie
statystykami w systemie ARENA. Zadanie to można zrealizować przy użyciu wzorca projektowego
Fasada (patrz rysunek 6.28), dzięki któremu połączymy w obiekcie Simpl eStati s t i csVaul t
metodę getStati sti csObj ect () z metodami zwracanych przez tę metodę obiektów Stati sti cs.
Następnie dodamy do klasy TournamentControl metodę update (), a do klasy Stati s t i csVi ew
metodę g e t S t a t () — obie delegujące swe wywołania do obiektu Simpl eStati s t i csVaul t.
Dzięki temu zarządzanie statystykami zostaje scentralizowane w ramach jednego obiektu sin-
gletonu. Co prawda, komplikuje to nieco interfejsy klas TournamentControl i Stati s t i csView
(patrz rysunek 10.23), jednak nadal pozostają one zgodne z zasadą 7 ± 2.

Rysunek 10.23. Klasa S t a t i s t i csVaul t jako fasada separująca obiekty brzegowe i sterujące od obli-
czania i przechowywania statystyk
482 Rozdział 10. • Odwzorowywanie modelu na kod

Jeżeli w przyszłości chcielibyśmy wprowadzić do systemu ARENA nowy zakres statystyk


— na przykład statystyki dotyczące poszczególnych meczów (Match)— wymagać to będzie
jedynie dodania nowego aspektu metody getStat () do klasy Stati s t i csVaul t. Co więcej, klasy
TournamentControl i Stati s t i csView zostały całkowicie odseparowane od poszczególnych
obiektów S t a t i s t i c s , co daje możliwość łatwej zmiany implementacji tych ostatnich, na
przykład wyposażenie ich w funkcje cache'owania przy użyciu odpowiednich obiektów
proxy (których zastosowanie w tej roli pokazaliśmy wcześniej na rysunku 10.5).
Tym samym rola obiektów Stati s t i cs zredukowana została do utrzymywania poszcze-
gólnych liczników (stanowiących w istocie pary „nazwa-wartość"), żadna z klas Stati s t i c s ,
PI ayer, Tournament i League nie jest już uczestnikiem skojarzenia n-arnego. W konsekwencji
ekstender klasy Game, tworząc specjalizację klasy S t a t i s t i c s dla nowej gry, musi skupić się
jedynie na formułach obliczania tej statystyki dla konkretnej pary „mecz-gracz".

10.6.3. Odwzorowywanie kontraktów w wyjątki


Z powodu pojawienia się w modelu projektu obiektów systemu ARENA nowego interfejsu, jakim
jest fasada S t a t i s t i csVaul t, musimy stworzyć dla tego interfejsu stosowny kontrakt i powią-
zać go z kontraktami istniejących klas. Na początek skupimy się na ograniczeniach dotyczących
klasy S t a t i s t i c s i ich propagacją do klasy S t a t i s t i csVaul t. Metoda Stati s t c s . g e t S t a t ()
skonstruowana została przy założeniu, że nazwa przekazana jako parametr identyfikuje znany
typ obiektu, stąd ograniczenie dla każdego aspektu metody getStat () w klasie Stati s t i csVaul t:

context Stati s t i csVault::getStat(name,game,piayer) pre:


getStatNames()->i ncludes(name)
context Stati s t i csVault::getStat(name,1eague,piayer) pre:
getStatNames()->i ncludes(name)
context Stati s t i csVaul t : -.getStat (name,tournament,pi ayer) pre:
getStatNames()->includes(name)

Podobne ograniczenie musimy nałożyć na metodę S t a t i s t i csVaul t . update() w celu


odzwierciedlenia ograniczeń nałożonych na metodę Stati s t i cs.update(). Tu warunek jest
tylko jeden — parametr wywołania powinien identyfikować mecz, który został już zakończony.

context StatisticsVault::update(match) pre:


match <> nul 1 and match.isCompletedQ

A następnej kolejności zajmiemy się ograniczeniami dotyczącymi pozostałych para-


metrów metod klasy S t a t i s t i csVaul t. Dopuszczamy, aby parametr identyfikujący gracza
miał wartość pustą (nul 1), co oznaczać będzie statystykę w kontekście wszystkich graczy danej
grupy (na przykład wszystkich członków ligi czy wszystkich uczestników turnieju). Pusta
wartość parametru reprezentującego turniej, ligę lub grę nie jest dopuszczalna, byłaby bo-
wiem sprzeczna z dziedziną aplikacyjną — obecnie w systemie ARENA nie ma możliwości zbie-
rania statystyk z zakresu wszystkich turniejów, wszystkich lig czy wszystkich gier. Ponadto
konkretny gracz reprezentowany przez parametr musi być powiązany z zakresem reprezen-
towanym przez pozostałe parametry (nie jest na przykład możliwe zbieranie statystyk gracza
w ramach turnieju, w którym nie jest zarejestrowany).
10.6. Analiza przypadku — system ARENA 483

context Stati sti csVault::getStat(name,game,player) pre:


game <> null and player <> null implies player.leagues.game->includes(game)
context Stati sti csVault::getStat(name,1eague,player) pre:
league <> null and player <> null implies 1eague.players->includes(player)
context S t a t i s t i c s V a u l t : : g e t S t a t ( n a m e , t o u r n a m e n t , p l a y e r ) pre:
tournament <> nul 1 and player <> null implies tournament.players->includes
^(player)

Ostatecznie powyższe ograniczenia musimy teraz odwzorować w kod generujący wyjątki


w razie ich niespełnienia. Wyjątki propagowane z obiektów S t a t i s t i cs będą przez klasę
S t a t i s t i csVaul t przekazywane bez sprawdzania do klasy wywołującej — i tak wyjątek
UnknownStati s t i c generowany może być przez metodę Stati s t i cs.getStat () i propagowany
jest przez metodę Stati sti csVaul t . g e t S t a t ( ) , podczas gdy wyjątki Inval idMatch i MatchNot
^Completed mogą być generowane przez metodę S t a t i s t i cs. update() i propagowane
przez metodę S t a t i s t i csVaul t . update(). Dla pozostałych ograniczeń definiujemy nową
klasę wyjątku — Inval idScope — reprezentującą przypadek, gdy parametr określający grę,
ligę lub turniej ma wartość pustą lub gracz reprezentowany przez parametr nie jest powiązany
z ligą i (lub) turniejem reprezentowanymi przez pozostałe parametry. Klasę tę wykorzystamy
przy sprawdzaniu warunków wstępnych w implementacjach poszczególnych aspektów metody
S t a t i st i cs Vault. g e t S t a t (). Publiczny interfejs klasy S t a t i s t i csVaul t, uwzględniający
opisane wyjątki, przedstawiony jest na listingu 10.4.

Listing 10.4. Publiczny interfejs klasy S t a t i s t i csVaul t

public class StatisticsVault {


public void update(Match m)
throws InvalidMatch, MatchNotCompleted { . . . }

publ ic Li s t g e t S t a t N a m e s Q {...}

public double getStat(String name, Game g, Player p)


throws UnknownStati s t i c, InvalidScope { . . . }

public double getStat(String name, League 1, Player p)


throws UnknownStatistic, InvalidScope { . . . }

public double getStat(String name, Tournament t , Player p)


throws UnknownStati s t i c, InvalidScope { . . . }

Zauważmy, że wyjątek Inval idScope wspólny jest dla trzech warunków wstępnych.
W rezultacie otrzymujemy podobne deklaracje wszystkich trzech aspektów metody getStat (),
co umożliwia klasie wywołującej jednolitą obsługę wyjątków generowanych przez wszystkie te
aspekty. Generalnie, wszystkie aspekty tej samej metody przeciążonej powinny mieć podobny
interfejs, wszystkie implementują bowiem tę samą operację, a różnią się jedynie typami para-
metrów wywołania.
484 Rozdział 10. • Odwzorowywanie modelu na kod

10.6.4. Odwzorowywanie modelu obiektowego w schemat bazy danych


Dokonaliśmy już transformacji n-arnego skojarzenia klasy S t a t i s t i c s (patrz rysunek 10.21)
na kod w języku Java; obecnie, wychodząc z tego samego modelu, dokonamy jego odwzoro-
wania na zbiór tabel bazy danych (patrz rysunek 10.24).

Rysunek 10.24. Schemat bazy danych odzwierciedlający n-arne skojarzenie klasy S t a t i s t i c s z ry-
sunku 10.21

W przypadku n-arnego skojarzenia nie jest możliwe zastosowanie skrytych powiązań,


rozpoczniemy więc od utworzenia osobnej tabeli S t a t i s t i c s , zawierającej klucze obce do
tabel reprezentujących klasy końcowe uczestniczące we wspomnianym skojarzeniu. Ponieważ
każda ze statystyk związana jest z dokładnie jedną gałęzią tego skojarzenia, każdy klucz obcy
będzie mieć postać dwóch kolumn, z których pierwsza (scopetype) wskazywać będzie typ
odnośnego obiektu, druga natomiast (scope) identyfikować będzie konkretny obiekt. Ze względu
na oszczędność miejsca i szybkość wyszukiwania, wartość w kolumnie scopetype będzie liczbą
całkowitą, której odwzorowanie na konkretny typ obiektu spoczywać będzie w gestii pod-
systemu odpowiedzialnego za magazynowanie danych. Zauważmy, że jest to lokalna decyzja
optymalizacyjna, niemająca żadnego związku z interfejsem klasy Stati s t i csVaul t. W kolumnie
scope zapamiętywany będzie natomiast klucz główny odnośnego obiektu. Ponieważ każda
statystyka jest de facto parą „nazwa-wartość", będziemy przechowywać wszystkie statystyki
w osobnej tabeli Stati s t i cCounters, posiadającej kolumny określające nazwę licznika (name),
jego wartość (val ue) i klucz obcy wskazujący odnośny rekord w tabeli S t a t i s t i cs.
Zauważmy, że jako podstawę do konstruowania opisanego schematu wykorzystaliśmy
model projektu obiektów, a nie kod źródłowy wygenerowany na bazie tego modelu. Uczyniliśmy
tak z trzech powodów:

• Model projektu obiektów ujmuje system z perspektywy dziedziny aplikacyjnej i jako


taki jest mniej podatny na zmiany niż kod źródłowy.
• Konstruując schemat bazy danych na podstawie modelu obiektów, używamy w tym
schemacie nazw pochodzących z dziedziny aplikacyjnej (na przykład S t a t i s t i cs)
zamiast nazw oznaczających obiekty realizacyjne (takich jak S t a t i s t i csVaul t).
10.7. Literatura uzupełniająca 485

Dzięki temu wyraźniejszy staje się związek wspomnianego schematu ze specyfikacją


wymagań.
• Transformacja n-arnego skojarzenia na kod klas w języku Java jest transformacją
typowo programistyczną, niemającą bezpośredniego odniesienia do schematu
relacyjnej bazy danych.

10.6.5. Wnioski
Transformując n-arne skojarzenie klasy Stati s t i cs na kod źródłowy programu i schemat bazy
danych, mogliśmy zauważyć, że:

• Zastosowanie jednej transformacji stwarza okazję do zastosowania innych trans-


formacji. Po przekształceniu n-arnego skojarzenia na oddzielne obiekty zdefiniowa-
liśmy fasadę separującą obiekty sterujące i brzegowe od szczegółów implementacji
zarządzania statystyką.
• Definiowanie nowych interfejsów wymaga propagowania otrzymywanych wyjątków.
Definiując nowe interfejsy, musimy się upewnić, że nie będą powodowały masko-
wania wyjątków. Gdy definiowaliśmy wspomnianą fasadę, konsekwentnie zadbaliśmy
o przekazywanie wszystkich otrzymywanych przez nią wyjątków UnknownStati s t i c
do klasy klienckiej.
• Schematy baz danych powinny być definiowane na bazie modeli obiektowych, a nie
na podstawie kodu źródłowego. Generalnie należy dążyć do tego, by koncepcje dzie-
dziny aplikacyjnej pozostawały widoczne w implementacji, bo zapewni to identy-
fikowalność kodu źródłowego i schematów baz danych z oryginalnymi wymaganiami.
Używanie nazewnictwa klas, atrybutów, metod i tabel zgodnego z konwencjami
dziedziny aplikacyjnej jest czynnikiem krytycznym dla utrzymywania integralności
koncepcyjnej systemu.

10.7. Literatura uzupełniająca


Historia refaktoryzacji datuje się na początek ogólnej transformacji programów. Wiele syste-
mów służących do transformowania programów koncentruje się na poprawianiu ich wydaj-
ności lub generowaniu ich kodu na podstawie formalnych specyfikacji. Książka H. Partscha
[Partsch, 1990] zawiera obszerne omówienie tego tematu.
Mimo iż systemy transformowania programów są dziś rozwiązaniami dojrzałymi, tylko
niewiele z nich umożliwia transformowanie modeli obiektowych. W książce J. Rumbau-
gha, M. Błahy, W. Premerlaniego, F. Eddy'ego i W Lorensena [Rumbaugh i in., 1991] po raz
pierwszy pojawiła się koncepcja takiej transformacji, później rozwinięta w książce M. Błahy
i W. Premerlaniego [Błaha i Premerlani, 1998] o koncepcje transformowania modeli obiek-
towych na schematy baz danych.
Refaktoryzacja, spopularyzowana przez M. Fowlera w książce [Fowler, 2000], jest tak
naprawdę odmianą transformacji programu zorientowanego obiektowo, przeprowadzaną
manualnie i przeplataną testowaniem. Refaktoryzacja jest jednym z filarów programowania
ekstremalnego (eXtreme Programming) opisanego w pracy K. Becka i C. Andresa [Beck
i Andres, 2005].
486 Rozdział 10. • Odwzorowywanie modelu na kod

10.8. Ćwiczenia
10.1. Na stronie W W W tabela złożona jest z wierszy, które z kolei składają się z komórek.
Szerokość i wysokość każdej komórki ustalana jest na bieżąco na podstawie jej
zawartości (tekstu, obrazka), a wysokość wiersza przyjmowana jest jako największa
wysokość jego komórki. Wynika stąd, że ostateczny układ tabeli na stronie W W W
może zostać ustalony dopiero wówczas, gdy cała wyświetlana zawartość zostanie po-
brana z internetu. Wykorzystując wzorzec projektowy Proxy, przedstawiony na
rysunku 10.5, zaproponuj model obiektowy i algorytm umożliwiający przeglądarce
rozpoczęcie wyświetlania tabeli, zanim jeszcze staną się ostatecznie wiadome roz-
miary jej komórek — oczywiście, gdy staną się znane, może być konieczne ponowne
narysowanie całej tabeli.
10.2. Zastosuj opisane w sekcji 10.4.2 transformacje do skojarzeń przedstawionych na
rysunku 10.25. Zakładamy, że wszystkie skojarzenia są dwukierunkowe i że mogą się
zmieniać w czasie życia obiektów. Utwórz kod źródłowy zarządzający tymi skoja-
rzeniami, wraz z deklaracjami klas, pól i metod i ich widzialności oraz implementa-
cjami metod.

Rysunek 10.25. Skojarzenia między komunikatami, folderami, skrzynkami pocztowymi i widokami


w hipotetycznej aplikacji klienckiej e-mail

10.3. Zastosuj opisane w sekcji 10.4.2 transformacje do skojarzeń przedstawionych na


rysunku 10.26. Zakładamy, że wszystkie skojarzenia są dwukierunkowe, lecz skoja-
rzenia agregacyjne pozostają niezmienne po utworzeniu obiektów; innymi słowy,
agregacje te muszą zostać zainicjowane w momencie tworzenia każdego obiektu. Na-
pisz kod źródłowy zarządzający tymi skojarzeniami, wraz z deklaracjami klas, pól
i metod, ich widzialności oraz implementacjami metod.

Rysunek 10.26. Skojarzenia między klasami League, Tournament, Round i Pl ayer


Bibliografia 487

10.4. Na rysunku 10.12 widoczny jest kod weryfikujący kontrakt dla metody Tournament.
^ a d d P l a y e r (). Napisz podobny kod dla innych ograniczeń związanych z klasą
Tournament, widocznych na listingu 9.2.
10.5. Dla kontraktów klas TournamentStyle i Round, omówionych w sekcji 9.6.2, napisz
kod weryfikujący warunki wstępne, warunki końcowe i niezmienniki.
10.6. Zaprojektuj schemat relacyjnej bazy danych dla modelu obiektowego przedstawionego
na rysunku 10.26. Zakładamy, że z każdym obiektem klasy League, Tournament,
PI ayer i Round związany jest unikalny identyfikator, a ponadto w klasie Tournament
i Round zdefiniowane są atrybuty s t a r t i end reprezentujące m o m e n t rozpoczęcia
i zakończenia (odpowiednio) turnieju i rundy. Wybierając między dostępnymi trans-
formacjami, uwidocznij kryteria tego wyboru.
10.7. Narysuj diagram klas reprezentujący poniższe fakty z dziedziny aplikacyjnej i od-
wzoruj je na schematy relacyjnej bazy danych.
• Projekt obejmuje pewną liczbą uczestników.
• Uczestnik może brać udział w projekcie jako menedżer projektu, kierownik
zespołu lub programista.
• W ramach projektu każdy programista i każdy kierownik zespołu muszą być
członkami co najmniej jednego zespołu.
• Uczestnik jednego projektu może uczestniczyć także w innym projekcie, być może
w innej roli: przykładowo programista w projekcie A może być kierownikiem
zespołu w projekcie B i menedżerem projektu C. Jego rola w każdym z projektów
pozostaje niezmienna.
10.8. Istnieją dwa podejścia do odwzorowywania skojarzeń na zbiory kolekcji. W sekcji
10.6.2 przedstawiliśmy odwzorowanie n-arnego skojarzenia klasy Stati s t i cs na dwie
klasy: S t a t i s t i c s , utrzymującą atrybuty skojarzenia, oraz S t a t i s t i csVaul t utrzy-
mującą stan powiązań między obiektami. W sekcji 10.4.2 opisaliśmy podejście alter-
natywne, polegające na reprezentowaniu skojarzenia w jednym lub obu obiektach
będących jego uczestnikami. W tym drugim przypadku mamy do czynienia z parą
rekursywnych metod zapewniających spójność obu struktur danych. Wykorzystaj
to alternatywne podejście do odwzorowania n-arnego skojarzenia Stati s t i cs w zbiór
kolekcji. Jakie są zalety obu wspomnianych rozwiązań i jakie kompromisy trzeba
rozstrzygać przy wyborze między jednym a drugim?

Bibliografia
[Beck i Andres, 2005] K. Beck, C. Andres Extreme Programming Explained: Embrace Change,
wyd. drugie, Addison-Wesley, Reading, MA, 2005.

[Błaha i Premerlani, M. Błaha, W. Premerlani Object-Oriented Modeling and Design for


1998] Database Applications, Prentice Hall, Upper Saddle River, NJ, 1998.

[Date, 2004] C. J. Date An Introduction to Database Systems, wyd. ósme, Addison-


Wesley, Reading, MA, 2004.

[Fowler, 2000] M. Fowler Refactoring: Improving The Design of Existing Code,


Addison-Wesley, Reading, MA, 2000.
488 Rozdział 10. • Odwzorowywanie modelu na kod

[Gamma i in., 1994] E. Gamma, R. Helm, R. Johnson,}. Vlissides Design Patterns: Elements
of Reusable Object-Oriented Software, Addison-Wesley, Reading, MA,
1994. Wydanie polskie Wzorce projektowe. Elementy oprogramowania
obiektowego wielokrotnego użytku, Helion 2010.

[Kramer, 1998] R. Kramer „iContract — The Java design by contract tooF' Technology
of Object-Oriented Languages and Systems, IEEE Computer Society Press,
str. 295, 1998.

[Partsch, 1990] H. Partsch Specification and Transformation of Programs,


Springer-Verlag, 1990.

[Rumbaugh i in., 1991] J. R u m b a u g h , M. Błaha, W . Premerlani, F. Eddy, W . Lorensen


Object-Oriented Modeling and Design, Prentice Hall, Englewood Cliffs,
NJ, 1991.

[Tolkien, 1995] J.R.R. Tolkien The Lord of The Rings, Harper Collins, 1995.
11.1. Wstęp: testowanie wahadłowców 492

11.2. O testowaniu ogólnie 494

11.3. Koncepcje związane z testowaniem 498


11.3.1. Usterki, błędne stany i awarie 500
11.3.2. Przypadki testowe 503
11.3.3. Namiastki testowe i sterowniki testowe 505
11.3.4. Poprawki 505
11.4. Aktywności związane z testowaniem 506
11.4.1. Inspekcja komponentu 507
11.4.2. Testowanie użyteczności 508
11.4.3. Testowanie jednostkowe 510
11.4.4. Testowanie integracyjne 519
11.4.5. Testowanie systemu 526

11.5. Zarządzanie testowaniem 531


11.5.1. Planowanie testów 532
11.5.2. Dokumentowanie testowania 532
11.5.3. Przydzielanie odpowiedzialności 536
11.5.4. Testowanie regresyjne 537
11.5.5. Automatyzacja testowania 538
11.5.6. Testowanie bazujące na modelach 539

11.6. Literatura uzupełniająca 541

11.7. Ćwiczenia 543

Bibliografia 544
Testowanie

Oprogramowanie jest gotowe.


Teraz tylko musimy sprawić, żeby zaczęło działać.
— zdanie wypowiedziane w czasie przeglądu projektu
Joint STARS E-8A FSD (http://www.the-crankshaft.info/
2010/04/electronicreconnaissanceobservation.html)

^Testowanie to proces wykrywania rozbieżności między rzeczywistym zachowaniem zaim-


plementowanego systemu a oczekiwanym zachowaniem odzwierciedlonym w jego mode-
lach. W ramach testowania jednostkowego znajdujemy różnice między specyfikacjami
obiektów a ich realizacjami w postaci komponentów; testy strukturalne mają na celu wy-
krywanie różnic między modelem projektowym systemu a zbiorem podsystemów stano-
wiących wynik jego dekompozycji; celem testów funkcjonalnych jest odnajdywanie różnic
między systemem a jego zachowaniem opisywanym przez model przypadków użycia, zaś
w ramach testów wydajnościowych system konfrontowany jest ze stawianymi m u wyma-
ganiami pozafunkcyjnymi, dotyczącymi między innymi (właśnie) wydajności.
Gdy któryś ze wspomnianych testów obnaży rozbieżność rzeczywistego systemu
z jego modelami, programiści poszukują usterki stanowiącej przyczynę tej rozbieżności
i przystępują do jej usuwania. Może się też tak zdarzyć, że system okaże się w pełni zgodny
ze swymi modelami, lecz mimo to daleki od spełnienia stawianych mu wymagań: przyczy-
ną tej rozbieżności jest wówczas sam model i to on wymaga zrewidowania.
Celem testowania systemu z punktu widzenia modelowania jest wykazanie niezgod-
ności ze stawianymi mu wymaganiami lub niespójności z jego modelami, czyli w praktyce
wczesne wykrywanie usterek, zanim okażą się źródłem poważnych problemów. Aktywność ta
różni się wyraźnie od innych opisywanych w poprzednich rozdziałach, czyli od analizowania
wymagań, projektowania, implementowania, komunikowania się i negocjowania: wszystkie
one mają charakter zdecydowanie konstruktywny, tymczasem testowanie jawi się jako aktyw-
ność na wskroś destrukcyjna, zmierza bowiem do załamania pracy systemu wszelkimi sposo-
bami. Ze względu na tę specyfikę powinna być więc wykonywana przez programistów nie-
uczestniczących w konstrukcji tegoż systemu.
Omawianie testowania rozpoczniemy od wyjaśnienia, dlaczego odgrywa tak istotną
rolę w procesie tworzenia oprogramowania. Po przedstawieniu ogólnego zarysu testowania
opiszemy szczegółowo koncepcje usterki, błędnego stanu, awarii i testu, a następnie zajmiemy
się aktywnościami składającymi się na proces testowania — planowaniem, projektowaniem
i przeprowadzaniem testów. Zaprezentujemy także profile UML jako mechanizm rozsze-
rzający, służący do opisu testowania opartego na modelach. Rozdział zakończymy przed-
stawieniem zagadnień menedżerskich związanych z testowaniem.
492 Rozdział 11. • Testowanie

11,1. Wstęp: testowanie wahadłowców


Testowanie to proces analizowania systemu i jego komponentów w celu wykrycia różnic
między zachowaniem specyfikowanym (oczekiwanym) a faktycznym (obserwowanym). Niestety,
wyczerpujące przetestowanie niebanalnego systemu jest zadaniem w praktyce niewykonalnym.
Po pierwsze, problem testowania jest problemem nierozstrzygalnym — dla danego systemu
nie sposób określić algorytmicznych reguł planowania i przeprowadzania testów. Po drugie —
testowanie, jako etap realizacji projektu, ograniczone jest harmonogramem i budżetem tego
projektu. Z konieczności więc bardzo często system wdrażany jest bez należytego przetestowania,
skutkiem czego zawiera usterki manifestujące się później jego użytkownikom pod postacią
rozmaitych błędów.
Warto wspomnieć w tym miejscu, iż pierwsza planowana misja promu kosmicznego
Columbia w lutym 1981 roku została odwołana z powodu problemów spowodowanych
usterkami, które nie zostały wykryte podczas tworzenia oprogramowania. Pierwotną przy-
czyną tych usterek okazała się zmiana, jaką programiści wprowadzili dwa lata wcześniej,
pomyłkowo resetując czynnik opóźniający z 50 do 80 milisekund. Wskutek tego pojawiło się
niebezpieczeństwo, że start promu może się nie udać z prawdopodobieństwem ll67. Usterka ta
pozostała niewykryta, mimo wielu tysięcy godzin poświęconych na testowanie całego systemu.
Gdy rozpoczynał się start, wspomniana usterka spowodowała problemy w synchronizacji
pięciu komputerów pokładowych, co spowodowało podjęcie decyzji o przerwaniu odliczania.
Poniżej przedstawiamy fragment artykułu, w którym Richard Feynman opisuje problemy
związane z testowaniem promów kosmicznych.

W trakcie prawie 250 000 sekund pracy zdarzyło się 16 poważnych awarii silników. Inżyniero-
wie bardzo poważnie przeanalizowali przyczyny tych awarii i podjęli wysiłki w celu ich jak naj-
szybszego usunięcia. Pomogły im w tym testowe analizy różnych atrap eksperymentalnych, za-
projektowanych specjalnie w związku ze wspomnianymi awariami, pozwalającymi na formułowanie
i weryfikowanie rozmaitych hipotez [...].

Testowanie mechanizmów wchodzących w skład samolotów wojskowych i cywilnych ma zwy-


kle charakter wstępujący — od szczegółów do ogółu, od k o m p o n e n t ó w do całości. Najpierw te-
stowane są wszechstronnie własności i ograniczenia materiałów używanych do produkcji naj-
bardziej krytycznych części tych mechanizmów (na przykład łopatek turbin); testy te, z użyciem
specjalnie spreparowanych atrap, mają także charakter niszczący. Bogatsi o tę wiedzę inżynie-
rowie projektują, a następnie testują podstawowe k o m p o n e n t y (takie jak łożyska); ewentualnie
wykryte usterki są korygowane i zmodyfikowany k o m p o n e n t poddawany jest weryfikacji w ra-
mach następnych testów. Ponieważ w danej chwili sprawdzany jest tylko jeden komponent, te-
stowanie nie jest ani zbyt skomplikowane, ani specjalnie kosztowne. Z przetestowanych pro-
stych k o m p o n e n t ó w budowane są i testowane k o m p o n e n t y bardziej złożone — i tak aż do
zbudowania produktu finalnego, i zapewnienia jego zgodności ze specyfikacją. Przedsięwzięcie
to m a duże szanse powodzenia głównie dlatego, że budowanie większych całości odbywa się
w oparciu o znane i przetestowane komponenty. Ponieważ większość poważnych awarii została
już usunięta w poprzednich etapach, korygowanie ewentualnych niezgodności produktu final-
nego ze specyfikacją nie powinno być ani zbyt skomplikowane, ani też wielce kosztowne.
11.1. Wstęp: testowanie wahadłowców 493

Budowanie i testowanie głównych silników p r o m u kosmicznego (SSME — Space Shuttle Main


Engine)1 odbywa się w diametralnie inny sposób — techniką zstępującą, od ogółu do szczegółów.
Zespół silników projektowany jest jako całość, przy relatywnie mało szczegółowym rozpoznaniu
materiałów i k o m p o n e n t ó w . Gdy więc pojawią się problemy z łożyskami, łopatkami turbin,
rurkami chłodzącymi i tym podobne, wykrycie i usunięcie ich przyczyny staje się bardzo kosz-
towne. Gdy na przykład pojawi się pęknięcie na którejś z łopatek turbiny tłoczącej ciekły tlen
pod wysokim ciśnieniem, w pierwszej kolejności pojawia się pytanie o jego przyczynę — czy jest
nią przyrodzona wada materiału, z którego wykonano łopatkę? Czy może jego utlenienie? Szok
termiczny przy starcie lub lądowaniu? Wibracje lub rezonans mechaniczny przy pewnej pręd-
kości? A może jeszcze coś innego? Ile czasu może minąć od inicjacji pęknięcia do poważnej ka-
tastrofy i od jakich czynników czas ten może być zależny? Wykorzystywanie gotowego produktu do
testów mających p o m ó c w udzieleniu odpowiedzi na te pytania wiąże się z ogromnymi kosztami
— nikt bowiem nie życzyłby sobie utraty całego silnika po to, by poznać przyczynę podobnych
usterek, a niestety, informacja taka jest konieczna po to, by zapewnić w przyszłości niezawodną
pracę silników. Co więcej, gdy przyczyna awarii już zostanie wykryta i okaże się nią na przykład
nieprawidłowy kształt łopatki, nie sposób zwykle usunąć tej przyczyny bez gruntownego prze-
projektowania całego mechanizmu.

Zespół głównych silników wahadłowca to bardzo skomplikowane urządzenie, od którego wymaga się
wysokiej niezawodności i do którego zaufanie wynika wprost z zaufania do jego poprzedników. Jest
ono jednak budowane na granicy — a wielu przypadkach poza granicą — dotychczasowych do-
świadczeń. Nie dziwią więc rozmaite usterki zdarzające się przy tej okazji — usterki trudne do
wykrywania i naprawiania, ze względu na „zstępującą" strategię konstruowania wahadłowca.
Zamierzenie projektantów zrealizowania przez SSME odpowiedników 55 misji (27 000 operacji,
symulujących 500-sekundowe misje) nie zostało wykonane. Silniki wymagają obecnie częstych
przeglądów i napraw kluczowych części, takich jak turbopompy, łożyska, osłony i pokrycia bla-
szane i tym podobnych. Wysokoprężne p o m p y paliwowe wymagały wymiany co trzeci lub
czwarty ekwiwalent misji (choć później były naprawiane), zaś p o m p y tłoczące ciekły tlen — co
sześć ekwiwalentnych misji. To zaledwie 10% początkowych zamiarów.

A r t y k u ł F e y n m a n a 2 przybliża ideę p r o b l e m ó w pojawiających się w związku z t e s t o w a n i e m


z ł o ż o n y c h systemów. I m i m o iż t y p o w y system i n f o r m a t y c z n y n i e b y w a tak s k o m p l i k o w a n y
j a k w a h a d ł o w i e c , w y z w a n i a s t o j ą c e p r z e d t e s t e r a m i są w o b u p r z y p a d k a c h p o d o b n e .
T e s t o w a n i e b y w a często p o s t r z e g a n e j a k o zajęcie w s a m raz dla p o c z ą t k u j ą c y c h p r o g r a -
m i s t ó w — m e n e d ż e r o w i e chętnie przydzielają d o tej f u n k c j i n o w y c h c z ł o n k ó w zespołu, bardziej
d o ś w i a d c z e n i wolą b o w i e m o d d a w a ć się z a j ę c i o m szacowniejszym, j a k analiza i p r o j e k t o w a n i e .
Filozofia t a k a p r o w a d z i j e d n a k d o wielu p r o b l e m ó w , b o w celu e f e k t y w n e g o p r z e t e s t o w a n i a
s y s t e m u tester m u s i p o s i a d a ć o d p o w i e d n i ą w i e d z ę n a t e m a t jego szczegółów, p o c z ą w s z y o d

1
Patrz http:/7pl.wikipedia.org/ wiki/Główne_silntki_promu_kosrnicznego — przyp. tłum.
2
Feynman napisał swój artykuł [Feynman, 1988] w czasie, gdy był członkiem prezydenckiej komisji
badającej przyczyny eksplozji promu kosmicznego Challenger w dniu 28 stycznia 1986 roku. Przyczynę
katastrofy upatrywał w uszczelce O-ring w zespole rakiet wspomagających, a dokładniej — w utracie
przez tę uszczelkę elastyczności wskutek zamarznięcia. W artykule, oprócz problemów związanych
z testowaniem głównych silników i rakiet wspomagających, zawarte są również uwagi na temat stop-
niowo zmieniających się kryteriów kluczowych testów akceptacyjnych oraz problemów wynikają-
cych z niedostatecznej komunikacji między kierownictwem projektu a jego wykonawcami w ramach
ściśle hierarchicznej organizacji.
494 Rozdział 11. • Testowanie

wymagań, poprzez decyzje projektowe, aż do problemów implementacyjnych. Tester musi po-


nadto cechować się zarówno dobrą znajomością technik testowania, jak i doświadczeniem
w efektywnym stosowaniu tych technik w zgodzie z budżetem i harmonogramem projektu.
W sekcji 11.2 przedstawiamy widok z lotu ptaka na problematykę testowania, w sekcji
11.3 definiujemy konkretne elementy modelu związanego z testowaniem — usterkę, błęd-
ny stan i awarię. Sekcja 11.4 zawiera opis aktywności występujących w procesie testowania:
testowanie jednostkowe, koncentrujące się na poszukiwaniu usterek w pojedynczych kompo-
nentach oraz testowanie integracyjne, skupiające się na błędach integracji tych komponentów
w kompletny system; tam też opisujemy testy związane z wymaganiami pozafunkcyjnymi
dotyczącymi użyteczności, wydajności i możliwości sprostania obciążeniom oraz z insta-
lowaniem systemu i testami pilotażowymi. Sekcję 11.5 poświęcamy menedżerskim aspek-
tom testowania.

11.2. O testowaniu ogólnie


Ogólnie pojmowana niezawodność (reliability) systemu jest miarą zgodności jego obser-
wowanego zachowania ze specyfikacją tego zachowania. Niezawodność oprogramowania
(software reliability) jest miarą prawdopodobieństwa, że będzie ono pracować bez awarii
w określonym przedziale czasowym, przy zapewnieniu określonych warunków [IEEE Std.
982.2-1988]. Awarią (failure) nazywamy każde odstępstwo zachowania obserwowanego od
zachowania specyfikowanego. Pod pojęciem błędnego stanu (erroneous state), zwanego często
po prostu błędem (error), rozumiemy taki stan systemu, że kontynuowanie jego pracy w tym
stanie prowadzi do awarii. Usterka (fault), zwana potocznie „defektem" lub „pluskwą" (bug),
to algorytmiczna lub mechaniczna przyczyna wejścia systemu w błędny stan. Celem testowa-
nia jest wykrywanie jak największej liczby usterek, które tym samym mogą być poprawione,
z korzyścią dla niezawodności systemu.
Pod pojęciem testowania (testing) rozumieć będziemy systematyczne wysiłki zmierzające
do wykrywania usterek w sposób planowy w implementowanym programie. Definicji tej często
przeciwstawiana jest inna, zgodnie z którą „testowanie to działanie zmierzające do wykazania,
że oprogramowanie nie zawiera błędów". Kolosalna różnica między obiema definicjami wi-
doczna jest aż nadto, ponadto druga jest o tyle nierealistyczna, że (przy obecnym systemie wie-
dzy i dostępnych narzędziach programistycznych) dla systemu o niebanalnych rozmiarach nie
istnieje jakikolwiek praktyczny sposób udowodnienia, że system ten wolny jest od usterek.
Zakrawać to może na swoisty paradoks, bo po pieczołowitym skonstruowaniu systemu
programiści (ci sami lub inni) próbują za wszelką cenę zniweczyć dzieło (swoje lub kolegów),
dezawuując niejako wykonaną uprzednio pracę. Faktycznie, w przeciwieństwie do aktywności
jednoznacznie konstruktywnych — analizowania wymagań, projektowania systemu i jego
obiektów, odwzorowywania modeli w środowisko wykonawcze — testowanie zdecydowanie
nacechowane jest destrukcją. Oto bowiem grupa testerów próbuje przekonać (siebie i kolegów),
że rezultaty owych konstruktywnych aktywności mniej lub bardziej rozminęły się z założonymi
celami — i naprawdę baaardzo rzadkie bywają przypadki, gdy fakty nie dostarczają im argu-
mentów na poparcie tej tezy. Testowanie wymaga więc zmiany sposobu myślenia w porówna-
niu z innymi aktywnościami: słowo „sukces", kojarzące się dotąd ze zbudowaniem niezawod-
nego, bezbłędnego systemu, nagle zyskuje nowy odcień, stanowiąc adekwatne określenia na
każdy przypadek wykrycia nowej usterki.
10.2. O odwzorowywanui ogólnie 495

W tym rozdziale potraktujemy testowanie jako aktywność bazującą na falsyfikacji modeli,


w sensie zasady K. Poppera o falsyfikacji teorii naukowych [Popper, 1992]. Zgodnie z tą zasadą,
przy każdej hipotezie naukowej należy dążyć do opracowania eksperymentu, który teorię tę
ewidentnie obala (falsyfikuje). Jeśli mimo usiłowań nie udaje się skonstruować takiego ekspe-
rymentu (w sensie fizycznym lub choćby tylko koncepcyjnym), hipoteza okazuje się coraz bar-
dziej godna zaufania i zostaje zaakceptowana jako uznana prawda naukowa (aż do ewentualnego
wynalezienia wspomnianego eksperymentu). Podobnie jest z testowaniem oprogramowania:
jest ono eksperymentem mającym obalić tezę, że oprogramowanie nie zawiera błędów. Jeżeli
mimo wszystko testowanie nie doprowadzi do wykrycia jakiejkolwiek usterki, stanowić to będzie
przesłankę na rzecz obdarzenia przedmiotowego oprogramowania większym zaufaniem
— być może na tyle dużym, iż zostanie przekazane użytkownikom do eksploatacji.
Niezawodność oprogramowania można zwiększyć, stosując techniki, które zaliczyć
można do jednej z trzech następujących grup.

• Unikanie usterek polega na ich wykrywaniu w sposób statyczny, bez związku z wyko-
nywaniem oprogramowania ani z jego modelami czy kodem źródłowym. Jest to de
facto zapobieganie usterkom za pomocą odpowiednich metodologii programistycz-
nych, zarządzania konfiguracją i weryfikacji.
• Wykrywanie usterek przy użyciu debugowania i testowania, które stanowią (od-
powiednio) niekontrolowany i kontrolowany eksperyment, to identyfikowanie błęd-
nych stanów systemu przed przekazaniem go do eksploatacji. Techniki wykrywania
usterek pomagają w ich odnajdywaniu, nie obejmują jednak likwidowania powodo-
wanych przez nie awarii. Zasadniczo techniki te stosowane są w czasie tworzenia
systemu, ale w pewnych przypadkach mogą być wykorzystywane także po jego
przekazaniu do eksploatacji. Przykładem ich zastosowania są czarne skrzynki w sa-
molotach, rejestrujące kilka ostatnich minut lotu.
• Tolerowanie usterek oznacza radzenie sobie przez pracujący system z konsekwen-
cjami tychże usterek. Przykładowo w systemie ze zwielokrotnionymi modułami kilka
redundantnych komponentów wykonuje równocześnie to samo zadanie, po czym
wyniki uzyskane przez wszystkie komponenty są ze sobą porównywane. Na pokładzie
promu kosmicznego znajduje się pięć komputerów wykonujących dwa różne rodzaje
oprogramowania w celu wykonywania tych samych zadań.

W tym rozdziale skupimy się na technikach wykrywania usterek, czyli na przeglądach


kodu i jego testowaniu. Przegląd (review) kodu to analiza wszystkich lub wybranych aspektów
systemu, na podstawie kodu źródłowego, jednakże bez jego wykonywania.. Istnieją dwie
odmiany przeglądu: wędrówki po kodzie i inspekcja kodu.
W ramach wędrówki po kodzie (walkthrough) programista w sposób nieformalny pre-
zentuje zespołowi przeglądowemu interfejs API, kod źródłowy i dokumentację „swego" kom-
ponentu. Zadaniem zespołu przeglądowego jest skomentowanie odwzorowania modelu
analitycznego i modelu projektu obiektów na kod źródłowy, w świetle przypadków użycia
i scenariuszy sformułowanych na etapie analizy wymagań. Inspekcja (inspection) kodu po-
dobna jest do wędrówki po kodzie, z tą różnicą, że prezentacja komponentu ma charakter
bardziej formalny. Programista nie prezentuje tu żadnych artefaktów (modeli, kodu czy do-
kumentacji), natomiast zespół przeglądowy odpowiedzialny jest za konfrontację interfejsu
496 Rozdział 11. • Testowanie

i kodu komponentu ze specyfikacją wymagań, a ponadto za analizę użytych algorytmów


w kontekście wydajności systemu oczekiwanej na podstawie wymagań pozafimkcyjnych. Ze-
spół przeglądowy weryfikuje też zgodność komentarzy towarzyszących kodowi z samym
kodem. Obecność programisty obowiązkowa jest tylko wówczas, gdy konieczne są dodatkowe
wyjaśnienia związane z użytymi przez niego algorytmami i strukturami danych.
Jak pokazuje doświadczenie, przeglądy kodu stanowią bardzo efektywną technikę wy-
krywania usterek: w przeprowadzonych w związku z tym tematem eksperymentach opisanych
przez M. E. Fagana [Fagan, 1976], T. C. Jonesa [Jones, 1977] oraz A. A. Potera, H. Siya,
C. A. Toman i L. G. Vottę [Porter i in., 1997] średnio 85% wszystkich znalezionych usterek
wykrywanych było właśnie w ramach takich przeglądów.
Debugowanie to technika zakładająca, że usterkę w oprogramowaniu zidentyfikować
można na podstawie jej związków przyczynowo-skutkowych z zaistniałą, nieoczekiwaną awarią.
Programista, dążąc do odtworzenia okoliczności wystąpienia awarii (z nadzieją na jej ponowne
wystąpienie), przechodzi w sposób kontrolowany przez poszczególne stany systemu, aż do
wejścia w błędny stan, w którym widoczna (mniej lub bardziej) staje się algorytmiczna lub me-
chaniczna przyczyna wspomnianej awarii. Ze względu na cel przyświecający debugowaniu, czyli
na weryfikowany aspekt systemu, dzieli się ono na dwa rodzaje: pierwszy rodzaj związany jest
z weryfikowaniem funkcjonalności systemu (w kontekście wymagań funkcyjnych), drugi z we-
ryfikowaniem jego wydajności (w kontekście wymagań pozafunkcyjnych).
Testowanie to technika mająca na celu wprowadzenie systemu w błędny stan w sposób
kontrolowany. Jej zadaniem jest wykrycie jak największej liczby usterek, zanim system przeka-
zany zostanie klientowi. Z definicji tej wynika jednoznacznie przytoczona wcześniej definicja
„sukcesu": sesja testowania jest sesją udaną, jeśli jej owocem jest wykrycie usterek — taki jest
właśnie punkt widzenia programistów podczas tworzenia systemu. Zauważmy jednak, iż ten
punkt widzenia zmienia się w sytuacji, gdy chce się przekonać klienta, że w systemie nie ma już
żadnych usterek, a przynajmniej nie udaje się takowych wykryć, i spełnia on wszelkie wymaga-
nia funkcyjne i pozafunkcyjne. Wystąpienie zdarzeń lub okoliczności przeczących tej tezie jest
wówczas oczywistą porażką.
Gdybyśmy jednak konsekwentnie trzymali się drugiej definicji, moglibyśmy (nawet pod-
świadomie) dobierać takie dane testowe, wobec których załamanie systemu byłoby mało praw-
dopodobne. W sytuacji odwrotnej, gdy każdą wykrytą usterkę poczytujemy sobie za sukces,
automatycznie dążymy do szukania przysłowiowej dziury w całym3. Dobry model testowy
charakteryzuje się tym, że składające się nań przypadki testowe zawierają takie właśnie „nie-
bezpieczne" dane — w przeciwnym razie wiele usterek nie miałoby po prostu szans na wykrycie.
Ze względu na potencjalnie olbrzymi zakres takich danych, obejmujących wartości z definicji
niepoprawne (na przykład ujemna liczba danych pomiarowych) czy wartości graniczne (na

3
To „szukanie dziury w całym" realizowane jest poprzez dobór jak najbardziej dziwacznych, kurio-
zalnych danych testowych; testerzy w tym kontekście nazywani bywają często „zawodowymi
idiotami" (nie ma to być obelga), posługują się bowiem danymi, których w innych okoliczno-
ściach mógłby użyć tylko kompletny idiota. Analogicznie, solidne oprogramowanie opierające się
skutecznie p r ó b o m załamania w ten sposób określane bywa popularnie m i a n e m „idiotoodporne-
go" (follproof) — przyp. tłum.
11.2. O testowaniu ogólnie 497

przykład brak danych pomiarowych)4, wyczerpujące testowanie nawet niewielkiego systemu


wymaga testów niesamowicie czasochłonnych.
Na rysunku 11.1 przedstawiono ogólną strukturę aktywności związanych z testowaniem.

• Planowanie testów to przydzielenie zasobów na potrzeby testowania i określenie


harmonogramu testów. Powinno być dokonywane jak najwcześniej, by zapewnić
dla testowania odpowiednio dużo czasu i odpowiednio wykwalifikowany personel.
Programiści mogą rozpocząć tworzenie przypadków testowych, gdy ustabilizuje się
model będący przedmiotem testowania.
• Testowanie użyteczności ma na celu znalezienie usterek w interfejsach użytkownika.
Często uzyskanie pożądanych efektów oczekiwanych od systemu jest utrudnione
dlatego, że użytkownicy nierozumiejący interfejsu mimowolnie wprowadzają błędne
dane.
• Testowanie jednostkowe ukierunkowane jest na wyszukiwanie usterek w poszcze-
gólnych komponentach i (lub) podsystemach, w kontekście przypadków użycia
dedykowanych tym komponentom (podsystemom).
• Testowanie integracyjne to poszukiwanie usterek, objawiających się w poszczegól-
nych komponentach w warunkach ich współpracy z innymi komponentami. Kulmi-
nacją testowania integracyjnego jest testowanie strukturalne, w ramach którego
sprawdzane jest współdziałanie wszystkich komponentów systemu. W czasie testów
integracyjnych i strukturalnych wykorzystuje się wiedzę zawartą w dokumencie
projektu systemu (SDD) w połączeniu ze strategią integracyjną określoną w planie
testów (Test Plan — TP).
• Testowanie systemu to testowanie działania wszystkich komponentów, postrzega-
nych jako pojedynczy system; celem testowania systemu jest wykrycie jego usterek
w kontekście scenariuszy opisywanych w deklaracji problemu, wymagań określonych
w specyfikacji oraz celów projektowych sformułowanych na etapie projektowania
systemu.
• Testowanie funkcjonalności to testowanie zgodności systemu z dokumentem
analizy wymagań (RAD) i jego spójności z podręcznikiem użytkownika.
• Testowanie wydajności ma na celu weryfikację spełnienia przez system wymagań
pozafunkcyjnych i pozostałych celów projektowych zawartych w SDD. Testy
funkcjonalne i testy wydajności wykonywane są przez programistów.
• Testowanie akceptacyjne i testowanie instalacji weryfikują zgodność systemu
z umową zawartą między klientem a wykonawcą projektu. Testy te wykonywane
są przez klienta, przy ewentualnej pomocy programistów.

4
C2ytelnikom, którym kwestia ta wydaje się oczywista, proponuję mały eksperyment: na podstawie znanego
I N N
wzoru na odchylenie standardowe cr = - ^ - ^ ( x / - x f ( x oznacza średnią arytmetyczną: )
I /=l /=i
napiszcie prosty program, a następnie zweryfikujcie jego zachowanie dla N = 0 (rzecz oczywista) oraz dla
N= 1 (rzecz już mniej oczywista, choć nie mniej ważna: dla N = 1 przyjmuje się (7= 0) — przyp. tłum.
498 Rozdział 11. • Testowanie

Rysunek 11.1. Aktywności testowania i związane z nimi produkty. Partycje diagramu wskazują wy-
konawców poszczególnych testów

11.3. Koncepcje związane z testowaniem


Na rysunku 11.2 widoczne są elementy modelu testowania:

• testowany komponent to część systemu, którą można wyodrębnić w celu testowania;


komponent ten może być obiektem, grupą obiektów, podsystemem lub grupą pod-
systemów,
11.4. Aktywności związane z testów_niem 499

Rysunek 11.2. Elementy modelu testowania

• usterka (fault), zwana także „pluskwą" lub „defektem", to błąd projektu lub błąd
kodowania, mogący powodować nienormalne zachowanie systemu,
• błędny stan (erroneous state) to manifestowanie się usterki w czasie wykonywania
systemu; błędny stan może być konsekwencją jednej lub kilku usterek, a jego następ-
stwem jest awaria,
• awaria (failure) to rozbieżność między specyfikacją a faktycznym zachowaniem
systemu; awaria może być wywołana przez jeden lub kilka błędnych stanów, choć
nie każdy błędny stan musi doprowadzić do awarii 5 ,
• przypadek testowy (test case) to zestaw danych wejściowych dla systemu oraz wyni-
ków oczekiwanych od systemu na podstawie tych danych; w czasie testowania roz-
bieżność między wynikami oczekiwanymi a faktycznie produkowanymi przez system
jest świadectwem usterki,
• namiastka testowa (test stub) to częściowa implementacja komponentów, od któ-
rych uzależnione jest działanie testowanego komponentu, natomiast sterownik te-
stowy (test driver)to częściowa implementacja komponentów, których działanie uza-
leżnione jest od testowanego komponentu, namiastki i sterowniki testowe umożliwiają
izolowanie testowanych komponentów (w czasie testowania) od reszty systemu,
• poprawka (correction) to zmiana wprowadzana do komponentu w celu naprawienia
(usunięcia) usterki; poprawki, mimo swego przeznaczenia, mogą stawać się źródłem
kolejnych usterek.

5
Należy zaznaczyć, że poza kręgami programistycznymi rzadko praktykuje się tak szczegółowe roz-
różnienie, kwalifikując wszelkie usterki, błędne stany i awarie jako (po prostu) „błędy".
500 Rozdział 11. • Testowanie

11.3.1. Usterki, błędne stany i awarie


Po formalnym zdefiniowaniu podstawowych koncepcji testowania zastanówmy się nad ich
praktycznym znaczeniem. Spójrzmy na rysunek 11.3: widzimy tam parę „niewyrównanych"
torów i nie trzeba wielkiej wyobraźni do przewidzenia konsekwencji przejażdżki pojazdu szy-
nowego po tym torowisku. Mimo iż (w kategoriach testowania) prawdopodobna katastrofa bę-
dzie ewidentną awarią, w sensie formalnym owo kuriozalne torowisko nie jest reprezentantem
ani awarii, ani błędnego stanu. Awaria to rozbieżność między sytuacją oczekiwaną a faktycznie
zaistniałą, tymczasem ani ta pierwsza nie została określona, ani też nie jesteśmy świadkami
tej drugiej. Błędny stan to sytuacja, w której dalsze kontynuowanie pracy systemu może
prowadzić do awarii; widzimy jednak tylko samo torowisko, po którym nic się jeszcze nie
porusza — nie ma bezpośredniej perspektywy awarii, nie ma więc mowy o błędnym stanie.

Rysunek 11.3. Przykład usterki — oczekujemy, że pojazd szynowy nie wykolei się

Generalnie, aby mówić o usterce, błędnym stanie czy awarii, musimy zarówno określić
oczekiwane zachowanie (w postaci przypadków użycia zapisanych w RAD), jak i zaobserwować
konkretne przejawy rzeczywistego zachowania. Do ewentualnego postrzegania niewyrównania
torowiska jako usterki sprecyzujmy więc to pierwsze w postaci przypadku użycia, przedstawione-
go w tabeli 11.1 i opisującego ruch pojazdu szynowego po pierwszym odcinku toru w kierunku
drugiego odcinka.

Tabela 11.1. Przypadek użycia Dri veTrai n specyfikujący oczekiwane zachowanie pojazdu szynowego

Nazwa przypadku użycia DriveTrain

Aktor uczestniczący T r a i n O p e r a t o r (maszynista)

Warunek wstępny Trai nOperator uruchamia pojazd za pomocą dźwigni na panelu sterowania.

Przepływ zdarzeń 1. Pojazd zaczyna poruszać się po pierwszym odcinku toru w kierunku
drugiego.
2. Pojazd przejeżdża na drugi odcinek toru.

Warunek końcowy Pojazd porusza się po drugim odcinku toru.


11.4. Aktywności związane z testów_niem 501

Mając sprecyzowane oczekiwane zachowanie pojazdu, możemy skonstruować przy-


padek testowy (patrz tabela 11.2) weryfikujący spełnienie naszych oczekiwań, czyli testują-
cy przemieszczanie się pojazdu od warunku wstępnego do warunku końcowego.

Tabela 11.2. Przypadek testowy weryfikujący przypadek użycia z tabeli 11.1

Identyfikator przypadku testowego DriveTrain

Lokalizacja testu http://wwwl2. in. tum. de/TrainSystem/test-cases/testl

Testowana cecha Nieprzerwane działanie pojazdu przez 5 sekund.

Kryteria zaliczenia!załamania testu Test zostaje zaliczony, jeśli pojazd poruszać się będzie
przez 5 sekund, pokonując co najmniej dwa odcinki toru.

Mechanizmy sterujące 1. M e t o d a S t a r t T r a i n ( ) zostanie wywołana przez


sterownik testowy S t a r t T r a i n (zlokalizowany w tym
samym katalogu, co przypadek testowy Dri veTrai n).

Dane testowe 1. Kierunek poruszania się pojazdu i czas trwania testu


odczytywane są z pliku http://wwwl2.in.tum.de/
TrainSy stem/test-cases/ input.
2. Jeśli zmienna DEBUG ma wartość t r u e , powinny być
wyświetlane komunikaty systemowe „wjeżdżam
na odcinek n" i „zjeżdżam z odcinka n' dla każdego n
w zakresie liczby odcinków testowanego torowiska.

Procedura testowania Test rozpoczyna się od kliknięcia hiperłącza wskazującego


lokalizację przypadku testowego. Dalszy przebieg testu
powinien odbywać się bez interwencji użytkownika.
Czas trwania testu nie powinien przekraczać 7 sekund.

Specjalne wymagania Do wykonania testu potrzebna jest namiastka testowa Engine


(reprezentująca pojazd szynowy).

Wykonanie przedstawionego przypadku testowego stanowić będzie dowód na to, że


system (torowisko) zawiera usterkę prowadzącą do awarii (pojazd wykolei się na styku
obu odcinków). Zauważmy, że sytuacja przedstawiona na rysunku 11.4 jest przykładem
błędnego stanu, ale nie awarii. Kuriozalne złączenie odcinków toru może być wynikiem
złej komunikacji między zespołami brygady budowlanej (każdy odcinek toru układany jest
przez inny zespół) lub błędnego zaimplementowania specyfikacji przez jeden z tych zespołów
(patrz rysunek 11.5) — obydwa te przypadki stanowią przykład usterki algorytmicznej. Usterki
algorytmiczne w postaci odczytywania wartości niezainicjowanych zmiennych, błędnego
formułowania warunków zakończenia pętli, testowania nieadekwatnego warunku i tym po-
dobne są chlebem powszednim każdego programisty. Usterka algorytmiczna może zostać
wprowadzona nie tylko do kodu, ale również do modelu; przykładem usterki modelu projektu
obiektów jest użycie specyficznego algorytmu prowadzącego do awarii, gdy dane wypełniające
konkretną strukturę wykroczą poza zakreślone dla niej ramy, wskutek nadmiernego obciążenia
systemu. Podobnie niedoszacowanie spodziewanego obciążenia systemu na etapie specyfiko-
wania wymagań pozafunkcyjnych jest usterką prowadzącą do awarii w sytuacji dużych wartości
tego obciążenia.
502 Rozdział 11. • Testowanie

Rysunek 11.4. Przykład błędnego stanu

Rysunek 11.5. Usterka może mieć pochodzenie algorytmiczne

Jeśli jednak nawet oba zespoły prawidłowo („zgodnie ze specyfikacją") ułożą („zaimple-
mentują") torowisko, mimo to, usterka może się w nim pojawić w codziennej eksploatacji, na
przykład wskutek osunięcia się podłoża spowodowanego trzęsieniem ziemi (patrz rysunek 11.6).
Innym, mniej spektakularnym niż trzęsienie ziemi przykładem usterki mechanicznej
jest usterka maszyny wirtualnej, na której działa system: jeśli nawet sam system zaimple-
mentowany jest prawidłowo, błędy odczytu lub zapisu na dysku czy błędna implementacja
którejś funkcji systemowej mogą zniekształcać pożądane zachowanie systemu. Usterki te-
go typu stają się szczególnie prawdopodobne w sytuacji, gdy sprzęt systemu projektowany
i tworzony jest równolegle z jego oprogramowaniem, bo wówczas maszyna wirtualna wciąż
11.4. Aktywności związane z testów_niem 503

Rysunek 11.6. Usterka może być również natury mechanicznej

jest bytem mało uchwytnym i nigdy nie można mieć gwarancji, że funkcjonuje tak, jak
oczekuje tego od niej oprogramowanie. Wreszcie, nawet przy bezbłędnej maszynie wirtu-
alnej i starannie przetestowanym oprogramowaniu może zdarzyć się usterka mechaniczna
w postaci wyłączenia zasilania. W tym kontekście pojęcia „usterki" i „awarii" nabierają rela-
tywnego znaczenia: awaria jednego komponentu (na przykład zasilacza) może stanowić
usterkę prowadzącą do wystąpienia awarii w innym komponencie (podsystemie).

11.3.2. Przypadki testowe


Przypadek testowy to zestaw danych wejściowych dla systemu oraz wyników oczekiwa-
nych od systemu na podstawie tych danych. Przypadek testowy może być traktowany jako
klasa (TestCase) posiadająca pięć atrybutów, którymi są (patrz tabela 11.3): nazwa (name),
lokalizacja (location), dane wejściowe (input), wyrocznia (oracie) i kronika (log). Nazwa
jednoznacznie identyfikuje przypadek testowy wśród innych; zalecane jest tworzenie na-
zwy przypadku testowego na podstawie nazwy testowanego komponentu — jeśli zatem te-
stujemy przypadek użycia Deposi t, odpowiedni przypadek testowy powinien mieć nazwę
Test_Deposi t, jeśli natomiast obejmuje on dwa komponenty A i B, powinien mieć nazwę
Test_AB. Atrybut location wskazuje położenie plików składających się na przypadek testowy
— może to być ścieżka w systemie plików lub lokalizator URL. Atrybut input opisuje zbiór
danych lub poleceń wprowadzanych do przypadku testowego przez aktora (którym może być
tester lub sterownik testowy). Spodziewana reakcja systemu, w postaci produkowanych przez
niego danych wynikowych, opisywana jest przez atrybut oracie. Rzeczywiste zachowanie sys-
temu i jego odniesienie do zachowania spodziewanego dokumentowane są chronologicznie
w kronice przypadku testowego, reprezentowanej przez atrybut 1 og.
504 Rozdział 11. • Testowanie

Tabela 11.3. Atrybuty klasy TestCase

Atrybut Znaczenie
name Nazwa przypadku testowego
location Kompletna ścieżka określająca lokalizację przypadku testowego
input Dane lub polecenia wejściowe
oracle Oczekiwane rezultaty testu, przeznaczone do konfrontacji z rzeczywistymi rezultatami
log Rzeczywiste wyniki produkowane przez system

Poszczególne przypadki testowe mogą być powiązane w dwojaki sposób: poprzez agrega-
cję lub poprzedzanie. Relacja poprzedzania, reprezentowana na diagramie przez skojarzenie
precede, określa wymaganą kolejność wykonywania dwóch testów. W przykładowym modelu
pokazanym na rysunku 11.7 przypadek testowy Test A składa się z dwóch przypadków składo-
wych TestAl i TestA2 (wykonanie obu oznacza wykonanie przypadku TestA, który nie posiada
fizycznej reprezentacji). TestA musi być wykonany przed rozpoczęciem wykonywania które-
gokolwiek z przypadków TestB i TestC, te jednak są od siebie wzajemnie niezależne i mogą być
przeprowadzanie równolegle. Dobry model testowy powinien posiadać jak najmniej skojarzeń,
gdyż wtedy składające się na niego testy mogą być w dużym stopniu zrównoleglone.

Rysunek 11.7. Skojarzenia między przypadkami testowymi w modelu testowania. TestA składa się z dwóch
testów TestAl i TestA2; testy TestB i TestC mogą być wykonywane niezależnie od siebie, przedtem
jednak konieczne jest wykonanie testu TestA

Zależnie od testowanych aspektów systemu lub modelu, przypadki testowe dzielą się na
czarnoskrzynkowe i białoskrzynkowe. Testy czarnoskrzynkowe (blackbox tests) skupiają się
wyłącznie na danych wejściowych i danych produkowanych przez komponent, w oderwaniu
od jego struktury wewnętrznej i tych aspektów jego zachowania, które są na zewnątrz niewi-
doczne. Testy białoskrzynkowe (whitebox tests) wnikają natomiast szczegółowo w wewnętrzną
strukturę i zachowanie komponentu, co ma przynieść zapewnienie, że niezależnie od przete-
stowania zewnętrznych aspektów zachowania komponentu, przetestowane zostaną wszystkie
powiązania między obiektami i wszystkie stany modelu dynamicznego. Testowanie biało-
skrzynkowe jest więc techniką wyraźnie bardziej obszerną; istotnie, większość testów biało-
skrzynkowych wykorzystuje dane niedające się wyprowadzić wyłącznie w oparciu o wyma-
gania funkcyjne. Testowanie jednostkowe łączy obie techniki: testy czarnoskrzynkowe
weryfikują funkcjonalność komponentu, podczas gdy testy białoskrzynkowe stanowią weryfi-
kację jego aspektów strukturalnych i dynamicznych.
11.4. Aktywności związane z testów_niem 505

11.3.3. Namiastki testowe i sterowniki testowe


Wykonywanie przypadku testowego dla komponentu lub grupy komponentów wymaga ich
odizolowania od reszty systemu. Ponieważ jednak w czasie testowania komponentu nie mo-
żemy zazwyczaj zupełnie abstrahować od jego interakcji z systemem, bo niektóre jego funkcje
mają sens jedynie w kontekście tych interakcji, musimy komponentowi temu stworzyć iluzję, iż
nadal znajduje się w swym „normalnym" środowisku. Zadanie to spełniają namiastki testowe
i sterowniki testowe. Sterownik testowy symuluje zachowanie tej części systemu, która wyko-
rzystuje testowany komponent: symulacja ta polega na przekazywaniu temu komponentowi
danych wejściowych i obsługiwaniu (najczęściej wyświetlaniu) otrzymywanych wyników. Na-
miastka testowa to symulacja komponentów, z których korzysta testowany komponent. Aby
udało się wytworzyć wspomnianą iluzję, komponenty wchodzące w skład namiastki testowej
muszą mieć API identyczne z API ich oryginałów, czyli identyczne sygnatury wszystkich metod
wykorzystywanych przez testowany komponent. Identyczność ta musi być zagwarantowana
w dłuższej perspektywie: jeśli zmieni się jakiś komponent (klasa czy podsystem), musi się od-
powiednio zmienić także jego (ewentualna) reprezentacja w ramach namiastki testowej. To
jedno z zadań zarządzania konfiguracją.
Implementowanie namiastek testowych nie jest zadaniem trywialnym. Nie zawsze bo-
wiem wystarczająca jest prymitywna atrapa ograniczająca swe działanie do wypisywania ko-
munikatów, że właśnie wywołana została ta czy inna jej metoda. Wręcz przeciwnie, w większości
sytuacji od namiastki testowej oczekuje się więcej konkretów: jeśli komponent A korzysta z kom-
ponentu B, to spodziewa się po nim wykonania pewnej pracy, której rezultaty zwrócone zostaną
jako wartości parametrów i (lub) wyniki metod. Jeśli komponent B nie zapewni tych warun-
ków, działanie komponentu A skończy się awarią, nie z powodu jego własnych usterek, lecz
dlatego, że komponent B nie symuluje należycie spodziewanych warunków zewnętrznych.
Co gorsza, nawet zwrócenie przez komponent B konkretnej wartości nie zawsze jest wy-
starczające — z tego samego powodu (zwracana wartość nie jest adekwatna do oczekiwań kom-
ponentu A) i z takim samym skutkiem (komponent A nie może działać poprawnie, bez winy
ze swej strony). W wielu przypadkach stworzenie namiastki testowej jest tak trudne, że prościej
użyć w tej roli oryginalnych komponentów. Zresztą, dla wielu komponentów dopiero po ich
ukończeniu tworzone są sterowniki i namiastki testowe, a jeśli dany komponent implemento-
wany jest w warunkach napiętego (lub przekroczonego) harmonogramu, nie są one tworzone
wcale. Aby zapobiec tej niekorzystnej tendencji, wiele metodologii programistycznych wymusza
wręcz tworzenie namiastek i sterowników równolegle z budowaniem komponentu oryginal-
nego; ma to ten pozytywny efekt uboczny, że komponent może być testowany na bieżąco,
w trakcie tworzenia, a wiele jego usterek może być wykrywanych i poprawianych wkrótce
po ich zaistnieniu, a nie dopiero po ukończeniu komponentu.

11.3.4. Poprawki
Oczywiście, konsekwencją znalezienia usterek jest ich naprawianie poprzez dokonywanie
poprawek. Poprawki te mogą mieć rozmaitą skalę: od kosmetycznych modyfikacji pojedynczej
klasy do kompletnego przeprojektowania całego podsystemu, zawsze jednak niosą ze sobą ry-
zyko wprowadzenia nowych usterek. Minimalizowanie tego ryzyka umożliwiają rozmaite
techniki, między innymi:
506 Rozdział 11. • Testowanie

• Śledzenie problemów, czyli — dla komponentu będącego przedmiotem zmian —


dokumentowanie każdej awarii, każdego błędnego stanu i każdej zidentyfikowa-
nej usterki, a także każdej dokonywanej poprawki. W połączeniu z zarządzaniem
konfiguracją umożliwia to programistom zawężenie obszaru poszukiwań no-
wych usterek. Śledzeniem problemów zajmiemy się dokładniej w rozdziale 13.
„Zarządzanie konfiguracją".
• Testowanie regresyjne, czyli ponowne wykonywanie testów zaliczonych uprzednio
przez komponent przed wprowadzeniem do niego poprawek. Ponowne zaliczenie
wszystkich tych testów stanowi silną przesłankę na rzecz tezy, że wprowadzona
poprawka nie spowodowała zmian w funkcjonalności komponentu. Testowanie
regresyjne ma niezwykle istotne znaczenie w przypadku tworzenia oprogramowania
w sposób iteracyjny, czyli między innymi w przypadku metodologii zorientowanych
obiektowo; konsekwencją tego jest konieczność zainicjowania testów jak najwcze-
śniej w procesie realizacji systemu i utrzymywania aktualnych zestawów testowych.
Niestety, testowanie regresyjne jest, jako czynność powtarzalna, dość uciążliwe,
szczególnie jeśli niektóre z testów nie są zautomatyzowane. Testami regresyjnymi
zajmiemy się dokładniej w sekcji 11.4.4.
• Racjonalizowanie zmian, czyli dokumentowanie racjonalizacji wszystkich poprawek
i ich związek z racjonalizacją samego komponentu. Dzięki dokładnej znajomości
wszystkich założeń, jakie przyjęte zostały w związku z tworzeniem komponentu,
programiści łatwiej mogą unikać wprowadzania nowych usterek. Racjonalizacją
zajmiemy się dokładniej w rozdziale 12. „Zarządzanie racjonalizacją".

11.4. Aktywności związane z testowaniem


Zajmiemy się teraz szczegółowo aktywnościami związanymi z tworzeniem przypadków testo-
wych i ich wykonywaniem oraz dokonywaniem poprawek. Opiszemy tu:

• Inspekcję komponentu, której zadaniem jest wyszukiwanie usterek w pojedynczym


komponencie, przez analizę jego kodu źródłowego (patrz sekcja 11.4.1),
• Testowanie użyteczności, prowadzące do zidentyfikowania różnic między tym, co
system faktycznie robi, a co robić powinien według oczekiwań jego użytkowników
(patrz sekcja 11.4.2),
• Testowanie jednostkowe, zmierzające do wykrywania usterek pojedynczego kom-
ponentu, przez wykonywanie związanych z nim przypadków testowych w warunkach
jego współpracy z namiastkami i sterownikami testowymi (patrz sekcja 11.4.3),
• Testowanie integracyjne, wykrywające usterki komponentów w warunkach ich
integracji (patrz sekcja 11.4.4),
• Testowanie systemu, koncentrujące się na kompletnym systemie, stawianych mu
wymaganiach funkcyjnych i pozafunkcyjnych oraz środowisku jego pracy (patrz
sekcja 11.4.5).
11.4. Aktywności związane z testów_niem 507

11.4.1. Inspekcja komponentu


Inspekcja komponentu ma za zadanie wykrycie usterek komponentu drogą analizowania jego
kodu źródłowego w warunkach formalnego zebrania. Inspekcję taką można przeprowadzić
przed testami jednostkowymi lub po ich zakończeniu. Historycznie pierwszą zaproponowaną
techniką inspekcji komponentów była metoda Fagana [Fagan, 1976], zgodnie z którą inspekcja
przeprowadzana jest przez zespół obejmujący programistów (w tym autora komponentu),
moderatora kierującego całym procesem i jednego lub więcej weryfikatorów, poszukujących
usterek w kodzie. Metoda Fagana obejmuje pięć następujących kroków.

• Streszczenie. Autor komponentu prezentuje w skrócie przeznaczenie i zakres funk-


cjonalny komponentu, a także cel inspekcji.
• Przygotowania. Weryfikatorzy zapoznają się z implementacją komponentu.
• Spotkanie. Narrator odczytuje kolejne instrukcje kodu źródłowego, parafrazując tym
samym jego wykonywanie, a każdy z uczestników może zgłaszać swoje zastrzeżenia
związane z poszczególnymi instrukcjami. Nad porządkiem zebrania czuwa moderator.
• Przeróbka. Autor komponentu wprowadza do niego niezbędne poprawki.
• Kontynuacja. Moderator ocenia jakość poprawek i może wskazać komponenty
kwalifikujące się do ponownej inspekcji.

Krytycznymi etapami opisywanej metody są przygotowania i spotkanie inspekcyjne.


W ramach przygotowań weryfikatorzy jedynie zapoznają się z kodem komponentu, nie myśląc
na razie o wyszukiwaniu jego usterek. Dopiero w czasie zebrania, gdy narrator odczytuje kolejne
instrukcje i wyjaśnia sens każdej z nich, weryfikatorzy (i inni uczestnicy) mogą zgłaszać swe
podejrzenia dotyczące usterek. Większość czasu poświęcona jest dyskutowaniu nad zasadnością
tych podejrzeń, w każdym razie na tym etapie nie podejmuje się próby naprawiania znalezio-
nych usterek. Zebranie inspekcyjne niekoniecznie musi ograniczać się do samego wyszukiwania
usterek: w fazie streszczenia autor komponentu może poprosić weryfikatorów o ocenę zgodno-
ści stworzonego kodu ze standardami kodowania i wskazanie ewentualnych fragmentów,
które jakkolwiek poprawne, kryją w sobie źródła nieoptymalności pod względem wykorzysty-
wania zasobów (pamięci, czasu procesora i tym podobnych).
Metoda Fagana postrzegana bywa jako czasochłonna, ze względu na długotrwałość przy-
gotowań i samego spotkania, poza tym jej efektywność zależna jest w dużej mierze od stopnia
przygotowania weryfikatorów. D. Parnas proponuje w związku z tym jej odmianę zwaną
aktywnym przeglądem projektu [Parnas i Weiss, 1985], w której rezygnuje się z gremialnego
zebrania z udziałem wszystkich członków; zamiast tego weryfikatorzy już w czasie fazy przy-
gotowań zajmują się poszukiwaniem usterek, a na zakończenie każdy z nich wypełnia kwestio-
nariusz ilustrujący stopień zrozumienia przedmiotowego komponentu. Następnie autor kom-
ponentu zbiera uwagi weryfikatorów, spotykając się z każdym z nich z osobna.
Zarówno metoda Fagana, jak i metoda aktywnego przeglądu projektu okazały się bar-
dziej efektywne niż wykrywanie usterek drogą „czystego" testowania. W krytycznych projek-
tach są one wykorzystywane wraz z testowaniem, jako że każda z tych aktywności sprzyja
wykrywaniu usterek innego typu.
508 Rozdział 11. • Testowanie

11.4.2. Testowanie użyteczności


Testowanie użyteczności to testowanie zrozumienia systemu przez jego użytkowników.
Zachowanie systemu jest tu konfrontowane nie ze specyfikacją, a z oczekiwaniami użytkowni-
ków. Ponieważ oczekiwania użytkowników trudno ująć w jakiś formalny model podlegający
testowaniu, testowanie to ma charakter empiryczny: reprezentanci użytkowników starają się
wykrywać problemy w drodze manipulowania interfejsem użytkownika lub jego symulacją.
Przedmiotem testów użyteczności są też detale interfejsu użytkownika, takie jak geometryczna
struktura ekranów, sekwencje interakcji czy też uwarunkowania sprzętowe — i tak na przykład
użytkownik przenośnego komputera czy innego urządzenia mobilnego chciałby mieć możli-
wość wydawania systemowi poleceń, nawet znajdując się w niewygodnej pozycji i mając
wolną tylko jedną rękę.
Przeprowadzanie testów użyteczności opiera się na klasycznym podejściu do przeprowa-
dzania kontrolowanych eksperymentów. Programiści najpierw ustalają zbiór celów testowych,
formułując swe oczekiwania pod adresem planowanych testów — na przykład uzyskanie in-
formacji dotyczących oceny określonych rozmiarów i kompozycji interfejsu, wpływu czasu
reakcji systemu na efektywność pracy użytkowników czy też przydatności systemu pomocy dla
nowicjuszy. Cele te testowane są następnie za pomocą serii eksperymentów, których uczestnicy
szkoleni są w zakresie wykonywania predefiniowanych zadań, czyli głównie oceny poszczegól-
nych elementów interfejsu. Programiści obserwują zachowanie użytkowników i kolekcjonują
metryki związane z ich wydajnością (między innymi średni czas reakcji, częstotliwość popeł-
niania pomyłek) oraz nieformalne opinie i sugestie dotyczące ewentualnych usprawnień,
co w swojej pracy opisał J. Rubin [Rubin, 1994].
Istnieją jednak dwie zasadnicze różnice między kontrolowanymi eksperymentami a te-
stami użyteczności. Po pierwsze, podczas gdy celem kontrolowanego eksperymentu jest falsyfi-
kacja pewnej hipotezy, przeważnie w oparciu o kryteria ilościowe, to testy użyteczności zmie-
rzają do uzyskania informacji jakościowej niezbędnej do usunięcia ewentualnych problemów
z użytecznością i generalnie do usprawnienia systemu. Po drugie, testy użyteczności odbywają
się w warunkach mniej rygorystycznych niż eksperymenty naukowe.
Udowodniono, że nawet seria krótkich, skoncentrowanych testów rozpoczynających się
już na etapie zbierania wymagań okazuje się niezwykle pomocna dla projektu. J. Nielsen for-
mułuje nawet termin „zdyskontowana inżynieria użyteczności" (discount usability engineering)
na określenie uproszczonych testów użyteczności, których koszt przeprowadzania jest tylko
ułamkiem kosztów pełnoprawnego studium, a które i tak są lepsze niż zupełny brak testów
[Nielsen i Mack, 1994]. Takie uproszczone testy mogą opierać się na „papierowych" scenariu-
szach oraz notatkach (zamiast prezentacji wideo) i koncentrować się na niewielkiej liczbie
zagadnień w celu zebrania ogólnych sugestii i wykrycia najważniejszych usterek (w przeciwień-
stwie do statystycznych miar istotności i innych zaawansowanych metryk).
Istnieją trzy główne rodzaje testów użyteczności.

• Test scenariusza. Jednemu użytkownikowi lub kilku z nich prezentuje się wizjonerski
scenariusz systemu. Programiści uważnie oceniają stopień rozumienia tego scenariusza
przez użytkowników w kategoriach szybkości percepcji, adekwatności reprezentowa-
nia ich modelu pracy i ogólnego nastawienia do obrazu nowego systemu. Wspo-
mniany scenariusz powinien być jak najbardziej szczegółowy i realistyczny. Może on
11.4. Aktywności związane z testów_niem 509

być zrealizowany w formie papierowych makiet 6 lub za pomocą prostych prototy-


pów, znacznie łatwiejszych w odbiorze niż środowisko programistyczne. Zaletą testów
scenariuszowych jest ich niewielki koszt i łatwa powtarzalność, wadą natomiast brak
możliwości rzeczywistej interakcji użytkowników z systemem, a także niezmienna
postać prezentowanych danych.
• Test prototypu. Użytkownikom prezentowany jest fragment oprogramowania, im-
plementujący kluczowe aspekty systemu. Przy wyborze owego fragmentu wyróżnia
się dwa podejścia: prototyp pionowy oznacza kompletną („głęboką") implementację
wybranego przypadku użycia, podczas gdy prototyp poziomy to „płytka" imple-
mentacja wybranej warstwy systemu. Prototypy pionowe wykorzystywane są do
ewaluacji kluczowych wymagań, takich jak wydajność i reaktywność systemu czyje-
go zachowanie w warunkach silnego obciążenia. Prototyp interfejsu użytkownika
jest natomiast przykładem prototypu poziomego, prezentuje bowiem interfejsy użyt-
kownika większości przypadków użycia, udostępniając jedynie szczątkową funk-
cjonalność. W niektórych przypadkach funkcjonalność ta może być sztucznie uzu-
pełniana drogą ludzkiej interwencji (której użytkownicy zwykle nie są świadomi):
przykładowo w systemach sterowanych głosem, w których podsystem rozpoznawa-
nia mowy nie został jeszcze w pełni zaimplementowany, realizację poleceń głosowych
może symulować operator ukryty za kurtyną — tego rodzaju „magiczne" prototypy
nazywane są potocznie (nie bez racji) prototypami z krainy Oz [Kelly, 1984]. Zaletą
testów prototypowych jest dostarczanie realistycznego widoku systemu oraz możli-
wość instrumentacji automatyzującej zbieranie wybranych metryk. Są one jednak
bardziej skomplikowane i kosztowniejsze w porównaniu z testami scenariuszowymi.
• Test produktu. Ten rodzaj testów podobny jest do testów prototypu, jednakże zamiast
prototypu przedmiotem testowania jest funkcjonalna wersja produktu. Testy te mogą
więc być przeprowadzane dopiero wówczas, gdy istnieją już gotowe produkty nadające
się do testowania (czyli w praktyce — gdy zrealizowana jest już większość systemu).
Produkty muszą być łatwo modyfikowalne, by można było szybko uwzględniać w nich
wyniki testów.

Każdy test użyteczności, niezależnie od rodzaju, obejmuje następujące elementy podsta-


wowe, o których pisze J. Rubin [Rubin, 1994]:

• zdefiniowanie celów testowych,


• wybranie reprezentatywnej grupy użytkowników,
• zbudowanie środowiska testowego, odzwierciedlającego lub symulującego rzeczywiste
warunki pracy testowanych komponentów,
• współdziałanie z użytkownikami realizującymi test,
• kolekcjonowanie i analizowanie wyników o charakterze jakościowym i ilościowym,
• rekomendowanie sposobów usprawniania systemu.

6
Poszczególne etapy scenariusza mogą być także odzwierciedlone w formie scenopisu rysunkowego
(.storyboard), jakiego używa się w filmach animowanych: kolejne szkice mogą być chronologicznie
ułożone na ścianie lub podłodze, wtedy są łatwo dostępne do oglądania i oceny. W średniej wielkości
pomieszczeniu można w ten sposób zmieścić nawet kilkaset takich szkiców.
510 Rozdział 11. • Testowanie

Celem przeprowadzenia testu użyteczności jest zazwyczaj porównywanie dwóch różnych


stylów interakcji systemu z użytkownikami, wartościowanie scenariusza lub prototypu pod
względem najlepszego i najgorszego elementu, identyfikowanie cech systemu szczególnie przy-
datnych dla nowicjuszy i użytkowników zaawansowanych oraz określenie zakresu niezbędnych
szkoleń użytkowników.

11.4.3. Testowanie jednostkowe


Testowanie jednostkowe (zwane również testowaniem modułów — unit testing) to testowa-
nie osobno poszczególnych cegiełek systemu, czyli jego obiektów i podsystemów. Testowanie
takie korzystne jest z co najmniej trzech powodów. Po pierwsze, przyczynia się do redukcji
komplikacji aktywności testowych przez skupienie się na prostszych jednostkach zamiast na
całości. Po drugie, prostszy obiekt testowany przekłada się na prostszy test, dzięki czemu
łatwiej wykrywać usterki i je naprawiać. Po trzecie wreszcie, jako że poszczególne obiekty mogą
być testowane niezależnie od siebie, ich testy można przeprowadzać równolegle, uzyskując
w ten sposób znaczną oszczędność czasu.
Podział systemu na testowane jednostki wynika wprost z modelu projektu obiektów i mo-
delu projektu systemu. Zasadniczo przetestowany powinien być każdy obiekt systemu, co nie
zawsze jest opłacalne w kontekście budżetu i harmonogramu; dokonując zatem wyboru obiek-
tów do testowania, należy w pierwszym rzędzie uwzględnić obiekty uczestniczące w przy-
padkach użycia. Każdy podsystem powinien być testowany dopiero wtedy, gdy zakończą się
poszczególne testy jego klas. Gotowe podsystemy (istniejące lub kupowane) należy traktować
jak komponenty o nieznanej strukturze wewnętrznej — dotyczy to zwłaszcza rozwiązań ko-
mercyjnych, których struktury wewnętrznej producent istotnie nie ujawnia programistom.
W literaturze znaleźć można różne propozycje dotyczące sposobu przeprowadzania testów
jednostkowych, poniżej przedstawiamy najważniejsze z nich: testowe klasy równoważności,
testowanie graniczne, testowanie ścieżek i testowanie sterowane stanem.

Testowe klasy równoważności

Matematycznie podział zbioru elementów na klasy równoważności odbywa się w ten spo-
sób, że każda klasa stanowi jego podzbiór, poszczególne Masy są parami rozłączne i każdy ele-
ment należy do dokładnie jednej klasy. Podstawą tego podziału jest pewna relacja równoważno-
ści, zachodząca między dwoma dowolnymi elementami tej samej klasy i niezachodząca między
dowolnymi dwoma elementami należącymi do różnych klas. W przypadku testowania czarno-
skrzynkowego wspomnianymi elementami są wszelkie możliwe konfiguracje danych wejścio-
wych, zaś dwie konfiguracje uważane są za równoważne sobie, jeśli wywołują takie samo działanie
testowanego systemu. W tym kontekście pojawia się pomysł podziału wszystkich możliwych
danych wejściowych na klasy równoważności i użycie (w charakterze przypadków testowych) dla
danej klasy jedynie kilku reprezentantów. Oczywiście, zarówno wspomniany podział, jak i wybór
reprezentantów jest specyfiką konkretnego systemu, niemniej jednak przy ich dokonywaniu
należy mieć na względzie wspomniane na wstępie własności klas równoważności, czyli:

• pokrycie — definiując klasy równoważności, należy uwzględnić wszelkie możliwe dane


wejściowe; innymi słowy, każda konfiguracja wejściowa musi dać się zaliczyć do do-
kładnie jednej klasy równoważności,
11.4. Aktywności związane z testów_niem 511

• rozłączność — żadna konfiguracja danych wejściowych nie może należeć do więcej


niż jednej klasy równoważności,
• reprezentatywność — jeżeli system prezentuje określone zachowanie dla danej konfi-
guracji danych wejściowych, powinien prezentować je dla wszystkich innych konfi-
guracji należących do tej samej klasy równoważności.

Dla każdej klasy równoważności należy uwzględnić co najmniej dwa elementy: jeden
reprezentujący „normalne" dane (z punktu widzenia poprawności) i drugi, reprezentujący dane
ewidentnie nieprawidłowe, których napotkanie system powinien skwitować wygenerowaniem
wyjątku. Jeśli trudno takiego wyboru dokonać, być może podzbiór elementów nie jest klasą
równoważności, bo został skonstruowany dość ogólnie i wymaga podziału na kilka prostszych
podzbiorów, które spełniać już będą własności klas równoważności.
Jako przykład zastosowania opisanej techniki rozpatrzmy testowanie metody zwracającej
liczbę dni w danym miesiącu danego roku kalendarza gregoriańskiego (patrz listing 11.1). Oba
parametry — rok i miesiąc — podane są w postaci liczb całkowitych i mogą przyjmować war-
tości o d -maxlnt d o maxlnt.

Listing 11.1. Interfejs metody obliczającej liczbę dni w danym miesiącu danego roku. Rok i miesiąc
podawane są w postaci parametrów będących liczbami całkowitymi.

c l a s s MyGregorianCalendar {

p u b l i c s t a t i c i n t getNumDaysInMonth(int month, i n t year) { ... }

Z własności kalendarza gregoriańskiego wynika natychmiast podział miesięcy na trzy


grupy. Pierwsza obejmuje miesiące o długości 31 dni (month = 1, 3, 5, 7, 8, 10,12), druga mie-
siące o długości 30 dni (month = 4, 6, 9, 11), trzecia dotyczy lutego. Wartość 0, wartości ujemne
i wartości większe niż 12 parametru month uważane są — oczywiście — za błędne. W podobny
sposób dla parametru y e a r wyróżnić można dwie grupy, obejmujące (odpowiednio) lata
przestępne i nieprzestępne; wartość 0 i wartości ujemne parametru year uważane są z defini-
cji za błędne.
Dla każdej ze wspomnianych grup musimy teraz wybrać po jednej poprawnej wartości
(na przykład luty, czerwiec, lipiec, rok 1901, rok 1904). Kombinacja obu grup daje w efekcie
sześć klas równoważności, których reprezentanty przedstawione są w tabeli 11.4.

Testowanie graniczne

Szczególnym przypadkiem reprezentantów poszczególnych klas równoważności są


elementy o wartościach granicznych, plasujące się niejako „na krawędziach" swych klas. Pro-
gramiści często zapominają o szczególnym znaczeniu takich elementów (na przykład roku
zerowego, pustego łańcucha, roku 2000 i tak dalej). W naszym przykładzie wartości graniczne są
512 Rozdział 11. • Testowanie

Tabela 11.4. Reprezentanty testowych klas równoważności dla metody getNumDaysInMonth ()

Klasa równoważności Parametr month Parametr year


Miesiące 31-dniowe w latach nieprzestępnych 7 1901

Miesiące 31-dniowe w latach przestępnych 7 1904

Miesiące 30-dniowe w latach nieprzestępnych 6 1901

Miesiące 30-dniowe w latach przestępnych 6 1904

Luty w latach nieprzestępnych 2 1901

Luty w latach przestępnych 2 1904

szczególnie interesujące w kontekście lat przestępnych. Wiadomo, że latami przestępnymi są


lata dzielące się bez reszty przez 4; wyjątkiem od tej zasady są lata będące pełnymi setkami, lecz
wyjątkiem od tego wyjątku są lata dzielące się przez 400 — i tak na przykład rok 2000 był
rokiem przestępnym, podczas gdy rok 1900 nie. Obie zatem wartości — 1900 i 2000 — są ide-
alnymi kandydatami na wartości graniczne parametru year. Przykładami wartości granicznych
parametru month są 0 i 13, plasują się bowiem „na krawędzi" klasy równoważności obejmującej
wartości niepoprawne. Zestaw danych z tabeli 11.4 uzupełniamy więc o wartości brzegowe,
widoczne w tabeli 11.5.

Tabela 11.5. Dodatkowe wartości (brzegowe) dla testowania metody getNumDaysInMonth ()

Klasa równoważności Parametr month P a r a m e t r year

Rok przestępny podzielny przez 400 2 2000

Rok nieprzestępny podzielny przez 100 2 1900

Niedodatnia wartość miesiąca 0 1291

Zbyt duża wartość miesiąca 13 1315

Podstawowym mankamentem obu technik — testowych klas równoważności i testowania


granicznego — jest traktowanie poszczególnych danych wejściowych jako danych izolowanych:
nie istnieją żadne korelacje między reprezentantami wybranymi do testowania. Tymczasem
często zdarza się tak, że awaria systemu wywołana jest nie pojedynczą porcją danych, lecz
określoną kombinacją tych porcji. Problem ten rozwiązywany jest za pomocą testowania
przyczynowo-skutkowego: dane wejściowe traktowane są jako „przyczyna", zaś „skutkiem"
jest określona postać wyniku. Testowanie takie bazuje na założeniu, że związek między danymi
wejściowymi a wynikami daje się ująć w postaci formuły boolowskiej. Czytelników zaintereso-
wanych tym tematem, a także inną techniką zwaną „przeczuwaniem błędów" (error guessing),
odsyłamy do książki G. J. Myersa [Myers, 1979] i innej literatury poświęconej testowaniu.

Testowanie ścieżek

Testowanie ścieżek to białoskrzynkowe wyszukiwanie usterek na podstawie implementacji


komponentu. U jego podstaw kryje się założenie, że wykonując co najmniej raz każdą możliwą
ścieżkę sterowania, doprowadzimy do wykrycia większości usterek poprzez awarie powo-
dowane tymi usterkami. Identyfikacja wspomnianych ścieżek wymaga jednak dobrej zna-
11.4. Aktywności związane z testów_niem 513

jomości kodu źródłowego i użytych w nim struktur danych. Punktem wyjścia do testowania
ścieżek jest graf przepływów. Węzły tego grafu reprezentują bloki wykonywalne kodu, zaś
krawędzie — przepływ sterowania między tymi blokami. Graf taki skonstruować można na
podstawie kodu źródłowego, odwzorowując instrukcje „decyzyjne" (czyli związane z wybo-
rem kierunku przepływu sterowania — między innymi i f, whi 1 e) na węzły. Bloki w ramach
instrukcji decyzyjnych (blok then, blok el se) odwzorowywane są w oddzielne węzły. Między
poszczególnymi węzłami występuje relacja poprzedzania. Na listingu 11.2 widoczna jest błędna
implementacja metody getNumDaysInMonth(), zaś na rysunku 11.8 przedstawiamy wspo-
mniany graf odpowiadający tej implementacji, w formie diagramu aktywności UML: bloki
kodu odpowiadają tu akcjom UML, decyzje — rozgałęzieniom, zaś przepływ sterowania
reprezentowany jest w formie przejść.

Listing 11.2. Przykład (błędnej) implementacji metody getNumDaysInMonth ()

public class MonthOutOfBounds extends Exception { ... };


public class YearOutOfBounds extends Exception { ... };
class MyGregorianCalendar {
publ ic static boolean i sl_eapYear(int year) {
boolean leap;
if ((year%4) == 0){
leap = true;
} else {
leap = false;
}
return leap;
}
public static int getNumDaysInMonth(int month, int year)
throws MonthOutOfBounds, YearOutOfBounds {
int numDays;
if (year < 1) {
throw new YearOutOfBounds(year);
}
if (month == 1 || month == 3 || month == 5 || month == 7 ||
month == 10 j | month == 12) {
numDays = 32;
} else if (month == 4 || month == 6 || month == 9 || month == 11) {
numDays = 30;
} else if (month == 2) {
if (isLeapYear(year)) {
numDays = 29;
} else {
numDays = 28;
}
} else {
throw new MonthOutOfBounds(month);
}
return numDays;
}
}
514 Rozdział 11. • Testowanie

Rysunek 11.8. Graf przepływów dla implementacji metody getNumDays I nMonth () z listingu 11.2 (funkcja
1 eap () zwraca wartość true, gdy jej argument identyfikuje rok przestępny i f a l se w przeciwnym razie)

Kompletne przetestowanie ścieżek wymaga sporządzenia takiego zestawu przypadków


testowych, by sterowanie przemierzyć co najmniej raz każdą z krawędzi w grafie przepływów.
Przypadki takie konstruuje się, analizując warunki związane z każdym rozgałęzieniem i dobie-
rając dane testowe zapewniające przejście każdej gałęzi. Przykładowo dla pierwszego rozgałęzie-
nia na rysunku 11.8 (year < 1) może to być wartość 0 parametru year (wyrażenie year < 1
ma wówczas wartość true) i wartość 1901 (wyrażenie to ma wówczas wartość fal se). W po-
dobny sposób wybieramy wartości months 1 i month=2 dla drugiego rozgałęzienia. W tych wa-
runkach dane wejściowe (year=0, month=l) powodują wystąpienie wyjątku throwl, zaś dane
(year= 1901, month=l) prowadzą do wykonania instrukcji {n=32 return}, co uwidacznia usterkę
kryjącą się w implementacji na listingu 11.2. Powtarzając ten proces dla każdego węzła, otrzy-
mamy zestaw przypadków testowych widoczny w tabeli 11.6.

Tabela 11.6. Przypadki testowe i odpowiadające im ścieżki przepływu sterowania dla grafu z rysunku 11.8

Przypadek testowy Ścieżka


(year = 0,month = 1) {throwl}
(year = 1901, month = 1) {n = 32 return}
(year = 1901, month = 2 ) {n = 28 return}

(year = 1904, month = 2 ) {n = 29 return}

(year = 1901,month = 4 ) {n = 30 return}

(year = 1901, month = 0 ) {throw2}


11.4. Aktywności związane z testów_niem 515

W podobny sposób możemy narysować graf przepływów dla operacji i sLeapYear ()


badającej przestępność roku i skonstruować przypadki testowe testujące jedyne rozgałę-
zienie w tym grafie (patrz rysunek 11.9). Zauważmy przy tym, iż przypadek testowy ( year
= 1901, month = 2 ) dla metody getNumDaysInMonth Opowoduje przejście sterowania przez
jedną z gałęzi tego rozgałęzienia.

Rysunek 11.9. Graf przepływów odpowiadający (błędnej) implementacji metody i sLeapYear () z listingu
11.2 i związane z tą metodą przypadki testowe. Kursywę wyróżniliśmy przypadki użyte już do testowania
metody getNumDaysInMonth ()

W ten oto sposób, konstruując przypadki testowe dla wszystkich ścieżek w implementacji
każdej z metod, możemy uporać się ze złożonością wynikającą z dużej liczby metod.
Opierając się na teorii grafów, można udowodnić, że minimalna liczba testów niezbęd-
nych do pokrycia każdego z rozgałęzień równa jest liczbie niezależnych ścieżek w grafie
[McCabe, 1976]. Liczba ta, zwana cyklomatyczną złożonością grafu (w skrócie CC, od Cyclomatic
Complexity), wyraża się wzorem 7 :

CC = liczba krawędzi - liczba węzłów + 2


gdzie 1 iczba węzłów to liczba punktów rozgałęzienia i akcji, zaś 1 iczba krawędzi to
liczba przejść w diagramie aktywności. Dla diagramu z rysunku 11.8 [AG54]wartość CC wyno-
si 6 i taka jest też liczba przypadków testowych dla metody getNumDays I nMont h () w tabeli 11.6.
Dla metody i sLeapYear () złożoność cyklomatyczna równa jest 2.
Porównując przypadki testowe uzyskane za pomocą metody klas równoważności (tabela
11.4) i przy uwzględnieniu warunków granicznych (tabela 11.5) z uzyskanymi za pomocą grafu

7
W ogólnym przypadku CC = liczba krawędzi - liczba węzłów + 2 * liczba składowych spójnych
grafu. Poszczególne składowe spójne reprezentują niezależne metody lub procedury. Graf spójny
jest swą jedyną składową, otrzymujemy więc dla niego prezentowany wzór — przyp. tłum.
516 Rozdział 11. • Testowanie

przepływów (patrz tabela 11.6 i rysunek 11.9), zauważymy pewną różnicę. W obu przypadkach
luty był obiektem szczególnego zainteresowania, ponieważ jednak implementacja metody
i sLeapYear () nie traktuje osobno lat podzielnych przez 100, testowanie ścieżek nie do-
prowadziło do żadnego przypadku testowego uwzględniającego tę klasę równoważności.
Naturalnym ograniczeniem testów białoskrzynkowych jest możliwość wykrywania jedy-
nie usterek ukrywających się na istniejących ścieżkach sterowania, takich jak błędna instrukcja
numDays=32. Test białoskrzynkowy nie wykryje błędów logicznych rodem z dziedziny aplika-
cyjnej, na przykład nieuwzględnienia faktu, że rok 1900 nie był rokiem przestępnym. Testowa-
nie ścieżek związane jest organicznie ze strukturą sterowania programu, jest więc bezsilne
wobec usterek związanych z naruszeniem niezmienników dotyczących struktur danych, na
przykład wobec przekroczenia granic tablicy. Generalnie jednak żadna metoda testowania
nie zagwarantuje wykrycia wszystkich usterek. W naszym przykładzie żadna z prezentowa-
nych metod nie wykryła usterki związanej z błędnym traktowaniem sierpnia przez metodę
getNumDaysInMonth().

Testowanie sterowane stanami

Ta technika testowania wynaleziona została stosunkowo niedawno i opisana przez


C. D. Turnera i D. J. Robsona [Turner i Robson, 1993]. Podczas gdy większość technik testo-
wania koncentruje się na konfrontowaniu wyników wieszczonych przez wyrocznię z tymi
faktycznie produkowanymi przez system, istotą opisywanej tu techniki jest porównywanie
stanów. W kontekście testowanej klasy przekłada się to na konstruowanie przypadków te-
stowych na podstawie diagramu stanów dla tej klasy. Dla każdego stanu określane są repre-
zentatywne zestawy bodźców uruchamiających każde z przejść wychodzących z tego stanu
(podobnie jak w przypadku klas równoważności). Po zrealizowaniu każdego przejścia atrybuty
obiektu „egzaminowane są" w celu stwierdzenia, czy znajduje się on w oczekiwanym stanie.
Na rysunku 11.10 widoczny jest diagram stanów i związane z nim testy dla zegarka
2BWatch opisywanego w rozdziale 2. „Modelowanie w języku UML". Diagram ten ukazuje
bodźce powodujące przejścia między wysokopoziomowymi stanami MeasureTime i SetTime;
nie są pokazane niskopoziomowe stany związane ze zmianą daty i czasu bądź to wskutek
upływu tego ostatniego, bądź też wskutek interwencji użytkownika. Pokazane w tabeli 11.7
bodźce zapewniają co najmniej jednokrotną trawersację każdego z przejść istniejących między
wymienionymi stanami. Po zmianie stanu kod instrumentacyjny dokonuje sprawdzenia,
czy jest to stan oczekiwany, i w przypadku rozbieżności uważamy, że wystąpiło załamanie.
Zauważmy, że niektóre przejścia (jak przejście nr 3) trawersowane są wielokrotnie, w celu
przywrócenia stanu SetTime, początkowego dla przejść 4., 5. i 6. Nie są generowane dane
testowe dla przejść prowadzących do stanu DeadBattery.
Testowanie sterowane stanami wiąże się z kilkoma trudnościami. Ponieważ stan te-
stowanego obiektu jest hermetyzowany, każdy z przypadków testowych musi wprowadzać
ów obiekt w stan stanowiący punkt wyjścia dla kolejnego przypadku. Poszczególne przy-
padki użycia muszą być więc odpowiednio zorganizowane w ciąg, co zresztą widoczne jest
w tabeli 11.7. Mimo iż testowanie sterowane stanami nie jest obecnie techniką zbyt popu-
larną, jest jednak techniką wielce obiecującą w odniesieniu do systemów zorientowanych
obiektowo i można spodziewać się jej upowszechnienia, gdy tylko pojawią się przeznaczo-
ne dla niej mechanizmy automatyzujące testy.
11.4. Aktywności związane z testów_niem 517

Rysunek 11.10. Diagram stanów dla przypadku użycia SetTime zegarka 2BWatch

Tabela 11.7. Kolejno wykonywane przypadki testowe dla zegarka 2BWatch; wymieniono tylko 8 po-
czątkowych bodźców

Przewidywany
Bodziec T e s t o w a n e przejście
stan wynikowy

brak 1. Przejście ze stanu początkowego MeasureTime

Naciśnięcie lewego przycisku 2. Przejście wewnętrzne, bez zmiany stanu MeasureTime

Równoczesne naciśnięcie 3. SetTime


obu przycisków

dwuminutowa bezczynność 4. MeasureTime

Równoczesne naciśnięcie 3. P o w r ó t do stanu umożliwiającego SetTime


obu przycisków testowanie kolejnych przejść

Równoczesne naciśnięcie 5. MeasureTime


obu przycisków

Równoczesne naciśnięcie 3. Powrót do stanu umożliwiającego SetTime


obu przycisków testowanie kolejnych przejść

Naciśnięcie lewego przycisku 6. Przejście wewnętrzne, bez zmiany stanu SetTime

Testowanie polimorfizmu

P o l i m o r f i z m w p r o w a d z a n o w ą komplikację d o testowania p r o g r a m ó w : d a n y k o m u n i k a t
m o ż e b y ć w i ą z a n y z r ó ż n y m i m e t o d a m i , zależnie o d jego d o c e l o w e j klasy. M i m o iż stwarza t o
o g r o m n e możliwości p o d w z g l ę d e m w i e l o k r o t n e g o w y k o r z y s t y w a n i a k o d u , w y m a g a k o n s t r u -
owania wielu p r z y p a d k ó w testowych, przetestowane b o w i e m m u s z ą być wszystkie możliwe
wiązania [Binder, 2000].
518 Rozdział 11. • Testowanie

Powróćmy na chwilę do sekcji 8.4.3, w której opisujemy zastosowanie wzorca projektowe-


go Strategia. Na rysunku 11.11 (będącym repliką rysunku 8.8) widzimy zastosowanie polimor-
fizmu do odseparowania kontekstu (reprezentowanego przez klasę NetworkConnection) od
konkretnej strategii implementacyjnej (reprezentowanej przez jedną z klas Ethernet, WaveLAN
albo UMTS i ukrywającej się za wspólnym interfejsem abstrakcyjnym Networklnterface). Wy-
wołanie Networklnterface. send (), mające na celu przesłanie ciągu bajtów przez podłączony
w danej chwili interfejs sieciowy, może w rezultacie prowadzić do wywołania jednej z trzech
metod Ethernet.send(), WaveLAN.send() albo UMTS.send().

Rysunek 11.11. Przykład polimorfizmu — zastosowanie wzorca Strategia do hermetyzowania róż-


norodności implementacyjnej interfejsu

Zastosowanie testowania ścieżek do operacji wykorzystującej polimorfizm wymaga


uwzględnienia wszystkich takich wiązań, czyli skonstruowania przypadków testowych powo-
dujących wywołanie odnośnej metody (sendQ) w każdej z możliwych subklas docelowych
interfejsu (Ethernet, WaveLAN i UMTS). W tym celu musimy dokonać na kodzie źródłowym
operacji stanowiącej niejako zaprzeczenie polimorfizmu, czyli po prostu explicite zaprogra-
mować wszelkie możliwe wiązania dla każdego wywołania metody polimorficznej (w czym
właśnie wyręczają nas mechanizmy języka programowania realizujące polimorfizm). Przykład
takiej preparacji kodu przedstawiony został na listingu 11.3: polimorficzny kod widoczny jest
w lewej jego części, w prawej natomiast widoczny jest efekt jawnego rozwinięcia wywołania
polimorficznego za pomocą rozbudowanej instrukcji i f, wybierającej jedno z odwołań do kon-
kretnej metody, zależnie od tego, jaką klasę reprezentuje w danej chwili obiekt ukrywający się
pod zmienną n i f.

Listing 11.3. Przykład transformacji wywołania polimorficznego (lewa część) na jawny wybór
konkretnej metody (prawa część), na potrzeby tworzenia przypadków testowych

public class NetworkConnectio public class NetworkConnection {

private N e t w o r k l n t e r f a c e n i f ; private N e t w o r k l n t e r f a c e nif;

void send (byte m s g [ ] ) { void send (byte m s g [ ] ) {


queue.concat(msg); queue.concat(msg);
11.4. Aktywności związane z testów_niem 519

if (nif.isReady()) { boolean ready = f a l s e ;


nif.send(queue); i f ( n i f instanceof E t h e r n e t ) {
queue.setLength(O); Ethernet eNif = (Ethernet)nif;
} ready = eNif.isReady();
} } else if ( n i f instanceof WaveLAN) {
} WaveLAN wNif = (WaveLAN)nif;
ready = wNif.isReady();
} else if ( n i f instanceof UMTS) {
UMTS uNif = (UMTS)ni f ;
ready = uNif.isReady();
}
if (ready) {
if ( n i f instanceof E t h e r n e t ) {
Ethernet eNif = (Ethernet)nif;
eNif.send(queue);
} else if (nif instanceof WaveLAN){
WaveLAN wNif = (WaveLAN)nif;
wNif.send(queue);
} else if (nif instanceof UMTS){
UMTS uNif = (UMTS)nif;
uNi f.send(queue);
}
queue.setLength(O);
}
}
}

Gdy już dokonamy rozwinięcia wszystkich wywołań polimorficznych w testowanym


kodzie, sporządzamy odpowiadający mu graf przepływów (patrz rysunek 11.12) i generujemy
przypadki testowe dla wszystkich możliwych ścieżek przepływu sterowania. Liczbę niezbęd-
nych przypadków testowych można przy tym redukować przez eliminowanie redundancji
— przykładowo wynik badania, na jakiej klasy instancję wskazuje zmienna ni f, jest taki sam
w obu częściach prawego fragmentu listingu 11.3, musimy więc tak naprawdę uwzględnić je-
dynie trzy ścieżki, a nie dziewięć, jak można by przypuszczać na podstawie grafu przepływów.
W sytuacji gdy testowany kod zawiera odwołania do wielu interfejsów i klas abstrak-
cyjnych, przy generowaniu grafu przepływów możemy doświadczyć istnej eksplozji ścieżek.
Tak oto polimorfizm odsłania swe drugie oblicze — prostota projektowania i możliwość two-
rzenia łatwo rozszerzalnych komponentów okupiona jest znacznym wysiłkiem związanym
z pokryciem wszystkich możliwych ścieżek sterowania w ramach testowania tych komponentów.

11.4.4. Testowanie inteo :q/]tvc


Testowanie jednostkowe (czyli u - vvunie modułów), zgodnie z nazwą, koncentruje się na
pojedynczych (jednostkowych) komponentach: programiści wykrywają usterki tych kompo-
nentów, wykorzystując opisane wcześniej techniki testowania. Gdy prowadzone w ten sposób
próby wykrywania kolejnych usterek okazują się bezowocne, przychodzi czas na przejście do
następnego etapu, czyli do testowania komponentów w grupach. W ramach takiego grupowego
520 Rozdział 11. • Testowanie

Rysunek 11.12. Graf przepływów odpowiadający rozwiniętej wersji kodu z listingu 11.3

testowania najprawdopodobniej dadzą znać o sobie kolejne usterki, których testowanie


jednostkowe nie wykryło z tej prostej przyczyny, iż namiastki i sterowniki testowe stanowią je-
dynie przybliżenie faktycznego otoczenia komponentów. Otoczenia, które teraz przybiera re-
alną, niesymulowaną postać. Poza tym testowanie jednostkowe nie jest w stanie obnażyć
usterek stanowiących konsekwencje błędnych założeń w stosunku do interfejsów wywoły-
wanych komponentów.
Testowanie integracyjne to zatem próba wykrywania usterek komponentów współpra-
cujących ze sobą w grupach różnej wielkości. Dwa komponenty, mające za sobą testy jednost-
kowe, zostają połączone i testowane integralnie w ramach testu dubletowego (zwanego także
— po prostu — testem dwójkowym); gdy tego typu testowanie wyczerpie już wszystkie możli-
wości wykrywania usterek, komponenty te dostają do towarzystwa trzeci komponent i cała
trójka poddawana jest testowi tripletowemu (trójkowemu), którego sukcesorem jest test kwa-
drupletowy (czwórkowy) obejmujący cztery współpracujące ze sobą komponenty — w ten oto
sposób kolejne komponenty integrowane są pojedynczo z większą całością, dzięki czemu ła-
twiej radzić sobie z kolejnymi problemami (których przyczyną okazuje się zwykle ostatnio do-
dany komponent).
Tworzenie namiastek i sterowników testowych związanych z tak pojętą systematyczną
integracją jest zajęciem czasochłonnym, dlatego też niektóre metodologie, z programowaniem
ekstremalnym (eXtreme Programming) na czele, wymuszają tworzenie sterownika przed roz-
poczęciem konstruowania samego komponentu [Beck i Andres, 2005]. Najistotniejszy jednak
wpływ na ogólną pracochłonność testowania integracyjnego ma wybór kolejności integrowania
poszczególnych komponentów; staranne zaplanowanie tej kolejności pozwala wydatnie redu-
11.4. Aktywności związane z testów_niem 521

kować zasoby niezbędne do przeprowadzania testów. W kolejnych sekcjach opiszemy dwie stra-
tegie (a właściwie grupy strategii) definiujące wspomnianą kolejność: integrowanie poziome,
w ramach którego integrowanie komponentów prowadzi do stopniowego budowania kolej-
nych warstw systemu, i integrowanie pionowe, w ramach którego konstruowane są stopniowo
poszczególne mechanizmy funkcjonalne systemu.

Strategie integrowania poziomego

Zaproponowano kilka odmian integrowania poziomego: testowanie hurtowe (big-bang


testing), testowanie wstępujące (bottom-up testing), testowanie zstępujące (top-down testing)
i testowanie „kanapkowe" (sandwich testing). Każda z tych strategii zakłada, że dekompozycja
systemu dokonana została w sposób hierarchiczny i podstawą zaliczenia komponentów do
poszczególnych szczebli tej hierarchii jest kierunek odwołań między nimi. Opisywane strategie
dają się jednak łatwo zaadaptować do dekompozycji niehierarchicznej. Na rysunku lł.13 wi-
doczna jest dekompozycja hierarchiczna, na przykładzie której zaprezentujemy cztery wy-
mienione strategie.

Rysunek 11.13. Przykład hierarchicznej, trój warstwowej dekompozycji systemu; warstwy reprezen-
towane są przez pakiety

Testowanie hurtowe to najprostsza ze strategii, zakładająca jednorazowe zintegrowanie


wszystkich komponentów zamiast ich integrowania stopniowego. Ponieważ każdy komponent
jest od początku na swoim miejscu, zbędne staje się tworzenie namiastek i sterowników testo-
wych — jest to jedyna zaleta tej metody. Jeśli bowiem w ramach testowania pojawią się awarie,
trudno będzie stwierdzić, czy powodująca je usterka związana jest z interfejsami komponentów,
czy z implementacją któregoś z nich. No i — oczywiście — trudno będzie wskazać konkretnego
winowajcę, bo podejrzane będą potencjalnie wszystkie komponenty. Z tego względu, w ogól-
nym rozrachunku, opłaca się sięgać po strategie zakładające integrowanie przyrostowe.
522 Rozdział 11. • Testowanie

Testowanie wstępujące (bottom-up testing) to testowanie rozpoczynające się u dołu


hierarchii i zmierzające ku jej szczytowi. Zintegrowaniu podlegają najpierw kolejne kompo-
nenty warstwy najniższej (po jednym na raz), po czym integracja przenosi się do warstwy
wyższej — i tak aż do kompletnego zintegrowania warstwy szczytowej. Niezintegrowane jeszcze
komponenty zastępowane są przez sterowniki; zauważmy, że nie jest potrzebne tworzenie
namiastek testowych.
Testowanie zstępujące (top-down testing) to w pewnym sensie odwrotność testowania
wstępującego. Integracja komponentów postępuje od warstwy szczytowej, w głąb hierarchii, aż
do osiągnięcia warstwy najniższej. Niezintegrowane jeszcze komponenty niższych warstw za-
stępowane są przez namiastki, nie są potrzebne sterowniki.
Zaletą testowania wstępującego jest łatwość wykrywania usterek związanych z inter-
fejsami: programista, „dokładając" kolejny komponent w testowanej warstwie, rozumie już do-
brze funkcjonowanie komponentów w warstwach niższych i założenia kryjące się w ich inter-
fejsach. Jeśli komponent wywołujący naruszy owe założenia, łatwo będzie naprawić związaną
z tym usterkę. Ponieważ jednak testowanie odbywa się w kierunku od komponentów ni-
skopoziomowych do wysokopoziomowych, najważniejsze podsystemy, w tym interfejs użyt-
kownika, testowane są na końcu; stosunkowo późno wykrywane są więc usterki najbardziej
kosztowne, czyli te, których naprawienie wymagać może zmiany kształtu dekompozycji syste-
mu (i w konsekwencji uznanie za niebyłe wszystkich przeprowadzonych już testów).
Testowanie zstępujące rozpoczyna się od komponentów tworzących interfejs użytkownika.
Ten sam zestaw testów, stworzony na podstawie wymagań stawianych systemowi, wykorzy-
stywany jest do testowania coraz bardziej złożonego zbioru podsystemów. Wadą tego podejścia
jest konieczność opracowywania dużej liczby namiastek testowych, zwłaszcza w przypadku
systemów o rozbudowanej funkcjonalności, gdzie niskopoziomowe komponenty implemen-
tują dużą liczbę metod. Nie trzeba dodawać, że namiastki te raczej nie będą wolne od błędów.
Na rysunkach 11.14 i 11.15 przedstawione są przykłady zastosowania integracji
wstępującej i integracji zstępującej. W pierwszym przypadku najpierw komponenty E, F i G
testowane są jednostkowo, po czym następuje test tripletowy trójki komponentów B-E-F
i dubletowy test pary komponentów D-G. W przypadku strategii zstępującej wszystko roz-
poczyna się od testu jednostkowego komponentu A, następnie wykonywane są trzy dubletowe
testy par A-B, A-C i A-D, po nich następuje kwadrupletowy test czwórki komponentów
A-B-C-D — i tak dalej. Obie strategie obejmują testowanie tych samych zależności między
podsystemami, tyle że każda w innej kolejności.
Rozwiązaniem łączącym zalety obu integracji — wstępującej i zstępującej — jest te-
stowanie kanapkowe (sandwich testing). By można było je zastosować, dekompozycja
systemu musi być dokonana w formie trzech warstw (lub trzech grup warstw): warstwy
docelowej, środkowej („szynki") i dwóch „kromek" chleba, czyli warstw dolnej i górnej.
Dla warstwy górnej prowadzona jest integracja zstępująca, polegająca na uzupełnianiu jej
komponentów poszczególnymi komponentami warstwy środkowej; podobnie dla warstwy
dolnej prowadzona jest integracja wstępująca, polegająca na uzupełnianiu jej komponen-
tów poszczególnymi komponentami warstwy środkowej. W ten sposób warstwa środkowa
pełni rolę zarówno namiastki dla warstwy górnej, jak i sterownika dla warstwy dolnej, co
eliminuje potrzebę tworzenia sterowników i namiastek dla obu skrajnych warstw (które
jednocześnie pełnią role sterownika (górna) i namiastki (dolna) dla warstwy środkowej).
11.4. Aktywności związane z testów _ niem 523

Rysunek 11.14. Testowanie wstępujące — kolejno testowane są: komponenty E, F i G, trójka B-E-Fi para D-G

Rysunek 11.15. Testowanie zstępujące — najpierw testowany jest k o m p o n e n t A, potem pary A-B, A-C
i A-D, potem czwórka A-B-C-D

Podstawową zaletą tej metody jest możliwość zrównoleglenia wielu testów (co widać
w przykładzie na rysunku 11.16), a także wcześniejsze niż w przypadku integrowania wstępują-
cego testowanie komponentów tworzących interfejs użytkownika. Testowanie kanapkowe ma
jednak tę wadę, że deprecjonuje warstwę środkową pod względem przetestowania jej poszcze-
gólnych komponentów. W przykładzie przedstawionym na rysunku 11.16 brakuje testu jed-
nostkowego komponentów B, C i D.
Zmodyfikowane testowanie kanapkowe (patrz rysunek 11.17) polega na indywidu-
alnym testowaniu każdej z warstw, przed poddaniem ich przyrostowemu testowaniu inte-
gracyjnemu. Zastosowanie tej strategii rozpoczyna się od fazy wstępnej, obejmującej ko-
lejno trzy następujące etapy:
524 Rozdział 11. • Testowanie

Rysunek 11.16. Strategia testowania kanapkowego. Żaden z komponentów warstwy środkowej (B, C i D)
nie jest testowany oddzielnie

• testowanie warstwy górnej przy użyciu namiastek symulujących warstwę środkową,


• testowanie warstwy środkowej przy użyciu sterowników i namiastek zastępujących
warstwy (odpowiednio) górną i dolną,
• testowanie warstwy dolnej, przy użyciu sterowników symulujących warstwę środkową.

Potem następują dwa testy integracyjne:

• testowanie warstwy górnej we współpracy z warstwą środkową; można tu ponownie


użyć testów dla warstwy środkowej, wykorzystywanych w środkowym etapie fazy
wstępnej,
• testowanie warstwy dolnej we współpracy z warstwą środkową; tu także można po-
nownie użyć testów dla warstwy środkowej, wykorzystywanych w środkowym etapie
fazy wstępnej.

Porównanie rysunków 11.16 i 11.17 pokazuje podobny stopień zrównoleglenia testów,


jak w przypadku oryginalnej metody „kanapkowej", jednakże w metodzie zmodyfikowanej
testy integracyjne warstw górnej i dolnej (z udziałem namiastek i sterowników zastępujących
warstwę środkową) mogą być zrównoleglone z testami jednostkowymi komponentów war-
stwy środkowej. Utrudnieniem metody zmodyfikowanej jest natomiast konieczność stwo-
rzenia sterowników i namiastek dla komponentów warstwy środkowej, gdy te uczestniczą
w testach jednostkowych. Jednak w ogólnym rozrachunku zmodyfikowana metoda kanapko-
wa prowadzi do krótszego czasu testowania w porównaniu z integrowaniem wstępującym
i zstępującym.
11.4. Aktywności związane z testów_niem 525

Rysunek 11.17. Zmodyfikowana metoda kanapkowa. Komponenty warstwy środkowej zostają pod-
dane testom jednostkowym przed zintegrowaniem ich z k o m p o n e n t a m i warstw sąsiednich

Strategie integrowania pionowego

W poprzedniej sekcji omawialiśmy strategie integrowania poziomego, w ramach którego


integracja komponentów odbywa się na poszczególnych warstwach, stanowiących wynik de-
kompozycji systemu. Ponieważ dekompozycja systemu wiąże się z przydzieleniem odpowie-
dzialności poszczególnym programistom, integrowaniem poziomym łatwo zarządzać, jako że
w ramach testów weryfikowane są interfejsy stanowiące podstawę porozumienia między ze-
społami. Podstawową wadą wszystkich strategii testowania poziomego jest brak jakiegokolwiek
aspektu funkcjonalności przejawianego przez system przez większą część procesu testowania
— funkcjonalność kwalifikująca system do wyemitowania w charakterze kandydata preten-
dującego do wersji statecznej (release candidate) pojawia się dopiero w późnej fazie testów.
Dla kontrastu, strategie integrowania pionowego koncentrują się na jak najszybszym
(„wczesnym") integrowaniu i dążeniu do realizacji poszczególnych przypadków użycia: dla
danego przypadku użycia budowany jest sukcesywnie komplet jego funkcjonalności, poprzez
stopniowe integrowanie komponentów składających się na interfejs użytkownika, logikę bizne-
sową, oprogramowanie pośrednie (middleware), magazynowanie danych i tym podobne. Za-
uważmy przy tym, że integrowanie pionowe jest czymś innym niż budowanie prototypu pio-
nowego na potrzeby testowania użyteczności (patrz sekcja 11.4.2), z tej prostej przyczyny, że
prototyp nigdy nie jest namiastką gotowego systemu i jako taki nigdy nie stanowi kandydatury
do emisji.
526 Rozdział 11. • Testowanie

Metodologia programowania ekstremalnego (eXtreme Programming), opisana przez


K. Becka i C. Andresa [Beck i Andres, 2005], organizuje integrowanie pionowe w kategoriach
zwanych historiami użytkownika. Historia użytkownika to nic innego jak wymaganie funkcyjne
sformułowane przez klienta; spełnienie tego wymagania przez zrealizowany system weryfiko-
wane jest właśnie w ramach testów integracyjnych towarzyszących każdej iteracji. Po zakoń-
czeniu wszystkich iteracji powstaje kandydat do emisji prezentowany klientowi. Podstawowym
problemem związanym z integrowaniem pionowym jest przyrostowe ewoluowanie projektu
systemu, wiążące się z częstym rewidowaniem podjętych wcześniej decyzji projektowych.
Problematyką wczesnego integrowania zajmiemy się dokładniej w rozdziale 13. „Za-
rządzanie konfiguracją".

11.4.5. Testowanie systemu


Testy jednostkowe i testy integracyjne zmierzają do wykrywania usterek tkwiących w poszcze-
gólnych komponentach i interfejsach między komponentami. Gdy wszystkie komponenty
zostaną zintegrowane, rozpoczyna się testowanie systemu mające na celu wykrycie ewentual-
nych rozbieżności gotowego systemu z jego wymaganiami funkcyjnymi i pozafunkcyjnymi.
Zauważmy, że testy w ramach integrowania pionowego są szczególnym przypadkiem testowa-
nia systemu, koncentrują się bowiem na nowych aspektach jego funkcjonalności. Testowanie
systemu obejmuje następujące aktywności:

• Testowanie funkcjonalności, czyli testowanie spełnienia wymagań funkcyjnych


określonych w dokumencie RAD,
• Testowanie wydajności, czyli testowanie zgodności systemu z wymaganiami po-
zafunkcyjnymi określonymi w SDD,
• Testowanie pilotażowe, czyli testowanie podstawowej funkcjonalności systemu przez
grupę wybranych użytkowników, w docelowym środowisku,
• Testowanie akceptacyjne wykonywane przez klienta w środowisku programistów
i obejmujące weryfikację użyteczności, funkcjonalności i wydajności systemu w kon-
tekście kryteriów akceptacji określonych w umowie z wykonawcą,
• Testowanie instalacji wykonywane przez klienta w jego własnym środowisku i obej-
mujące weryfikację użyteczności, funkcjonalności i wydajności. Jeśli system udo-
stępniony jest w tym celu niewielkiej grupie zaufanych użytkowników, testowanie
nazywane jest popularnie beta-testowaniem.

Testowanie funkcjonalności

Testowanie funkcjonalności, zwane także testowaniem wymaganiowym, ma na celu


wykrycie różnic między zachowaniem systemu a stawianymi mu wymaganiami funkcyjnymi.
Testowanie funkcjonalności jest techniką czarnoskrzynkową: przypadki testowe formułowane
są na podstawie modelu przypadków użycia. W systemie o skomplikowanych wymaganiach
funkcyjnych nie jest zwykle możliwe przetestowanie wszystkich przypadków użycia w kontek-
ście poprawnych i niepoprawnych danych wejściowych, celem testera jest więc zidentyfikowanie
11.4. Aktywności związane z testów_niem 527

tych, które są istotne dla użytkownika, i tych dających duże prawdopodobieństwo wykrycia
usterek. Zauważmy, że testowanie funkcjonalności jest czymś różnym od innych testów rów-
nież mających swe źródło w modelu przypadków użycia — testów spełnienia wymagań poza-
fiinkcyjnych związanych z użytecznością, opisanych w rozdziale 4.: te ostatnie konfrontują
system z oczekiwaniami jego użytkowników, podczas gdy testowanie systemu weryfikuje jego
zgodność z wymaganiami zapisanymi w dokumencie RAD.
W celu zbudowania przypadków testowych dla testowania funkcjonalności przeprowa-
dzamy inspekcję modelu przypadków użycia i identyfikujemy te instancje przypadków użycia,
które wydają się najbardziej prawdopodobnymi źródłami usterek; identyfikacja taka opiera się
na postępowaniu podobnym do tworzenia testowych klas równoważności i testów granicznych
(które opisywaliśmy w sekcji 11.4.3). Wspomniane przypadki testowe powinny uwzględniać
zarówno „normalne", jak i wyjątkowe sytuacje. Rozpatrzmy przykładowo wielokrotnie już
cytowany system T i c k e t D i s t r i b u t o r realizujący automatyczną sprzedaż biletów w metrze
(patrz rysunek 11.18). Jego normalne funkcjonowanie ujęte jest w ramach przypadku użycia
PurchaseTicket, opisującego kroki wykonywane przez podróżnego (czyli aktora Passenger)
w celu otrzymania żądanego biletu. Przypadki użycia TimeOut, Cancel, OutOfOrder i NoChange
opisują natomiast sytuacje wyjątkowe związane z (kolejno) zbyt długą bezczynnością podróż-
nego, anulowaniem przez niego transakcji, awarią automatu i brakiem bilonu odpowiedniego
do wydania reszty.

Rysunek 11.18. Przykład modelu przypadków użycia związanego z automatem sprzedającym bilety

W tabeli 11.8 znajduje się przypadek użycia PurchaseTicket opisujący normalny prze-
bieg interakcji między podróżnym a automatem biletowym, czyli między aktorami Passenger
i Ti cketDi s t r i butor. Uważne przestudiowanie tego opisu pozwala wykryć co najmniej
trzy cechy automatu, których realizacja może nie potwierdzić się w testowaniu:

1. Podróżny przed wrzuceniem nominału może nacisnąć kilka przycisków reprezentu-


jących różne strefy, na co automat powinien zareagować honorowaniem tylko przyci-
sku naciśniętego najpóźniej.
528 Rozdział 11. • Testowanie

2. Podróżny po rozpoczęciu wrzucania nominałów, lecz przed wrzuceniem ostatniego


nominału, może nacisnąć przycisk identyfikujący strefę inną niż przycisk ostatnio na-
ciśnięty. W takiej sytuacji automat powinien zwrócić wszystkie nominały wrzucone
dotychczas w ramach trwającej transakcji.
3. Podróżny może wrzucić więcej nominałów, niż wynika to z ceny biletu. Automat
powinien wówczas zwrócić nadmiarowe nominały na takiej samej zasadzie jak wy-
dawanie reszty.

Tabela 11.8. Przykładowy przypadek użycia zaczerpnięty z modelu przypadków użycia dla automatu
sprzedającego bilety

Nazwa przypadku użycia PurchaseTicket

Warunki wstępne 1. Podróżny stoi przed automatem.


2. Podróżny posiada wystarczającą ilość pieniędzy do zakupu biletu.

Przepływ zdarzeń 1. Podróżny naciska jeden lub więcej przycisków reprezentujących strefy.
Dla automatu wiążący jest jedynie przycisk naciśnięty najpóźniej.
2. Automat wyświetla kwotę do zapłaty.
3. Podróżny wrzuca nominały.
4. Jeśli podróżny naciśnie przycisk identyfikujący strefę przed
wrzuceniem nominałów składających się na żądaną kwotę, automat
zwraca nominały wrzucone dotychczas przez podróżnego.
5. Jeżeli podróżny wrzuci nominały składające się na kwotę większą
niż wymagana, automat wydaje resztę.
6. Automat drukuje bilet.
7. Podróżny odbiera bilet i ewentualną resztę.

Warunek końcowy Podróżny posiada żądany bilet.

W tabeli 11.9 przedstawiony jest przypadek testowy PurchaseTi cket_CommonCase


weryfikujący trzy wymienione cechy. Zwróćmy uwagę, że przypływ zdarzeń odzwierciedla
zarówno dane wejściowe (czyli czynności wykonywane przez podróżnego w kontekście auto-
matu), jak i dane wynikowe (czyli oczekiwaną reakcję automatu na te sygnały). Podobne przy-
padki testowe można także skonstruować dla „wyjątkowych" przypadków użycia NoChange,
OutOfOrder, TimeOut i Cancel.
Przypadki testowe, takie jak PurchaseTi cket__CommonCase, konstruowane są na podstawie
przypadków użycia, także tych, które reprezentują sytuacje wyjątkowe. Konieczne jest zapew-
nienie identyfikowalności tych przypadków testowych, czyli czytelne ich powiązanie z od-
nośnymi przypadkami użycia. Gdy zmodyfikowany zostanie jakiś przypadek użycia, trzeba bę-
dzie odpowiednio zmienić powiązany z nim przypadek testowy.

Testowanie wydajności

Testowanie wydajności to weryfikowanie zgodności systemu z celami projektowymi


zdefiniowanymi na etapie jego projektowania. Ponieważ cele projektowe określane są na
podstawie wymagań pozafunkcyjnych, przypadki testowe dla testów opisywanej kategorii
mogą być formułowane zarówno na podstawie dokumentu SDD, jak i na postawie doku-
mentu RAD. Testowanie wydajności obejmuje następujące testy szczegółowe.
11.4. Aktywności związane z testów_niem 529

Tabela 11.9. Przykładowy p r z y p a d e k testowy s k o n s t r u o w a n y na podstawie p r z y p a d k u użycia


PurchaseTicket

Nazwa przypadku testowego PurchaseTicket CommonCase

Warunki wstępne 1. Podróżny stoi przed dystrybutorem.


2. Podróżny posiada dwie pięciodolarówki i trzy dziesięciocentówki.

Przepływ zdarzeń 1. Podróżny naciska kolejno przyciski stref 2, 4, 1 i 2.


2. Automat powinien wyświetlić kolejno kwoty 1 . 2 5 , 2 . 2 5 , 0 . 7 5 i l . 2 5
dolarów.
3. Podróżny wrzuca pięciodolarówkę.

4. Automat wydaje trzy jednodolarówki, trzy ćwierćdolarówki i drukuje


bilet na dwie strefy.
5. P o d r ó ż n y powtarza kroki od 1. do 4., używając drugiej
pięciodolarówki.
6. P o d r ó ż n y powtarza kroki od 1. do 3., używając czterech
ćwierćdolarówek i trzech dziesięciocentówek. Automat drukuje
bilet na dwie strefy i wydaje pięciocentówkę reszty.

7. Podróżny wybiera strefę 1. i wrzuca jednodolarówkę. Automat


drukuje bilet na jedną strefę i zwraca ćwierćdolarówkę.

8. Podróżny naciska przycisk strefy 4. i wprowadza dwie jednodolarówki


i ćwierćdolarówkę. Automat drukuje bilet na cztery strefy.
9. Podróżny naciska przycisk strefy 4. Automat wyświetla kwotę
2,25 dolarów. Podróżny wrzuca jednodolarówkę, pięciocentówkę
i naciska przycisk strefy 2. Automat zwraca wrzucone jednodolarówkę
i pięciocentówkę i wyświetla kwotę 1,25 dolara.

Warunek końcowy Podróżny dysponuje trzema biletami na dwie strefy, jednym biletem
na jedną strefę i jednym biletem na cztery strefy.

• Testy przeciążeniowe sprawdzają zdolność systemu do właściwego reagowania na


masywny strumień równoczesnych żądań. Jeżeli przykładowo dla systemu informa-
cyjnego wykorzystywanego przez dealerów samochodów wymaga się obsługi 6000
klientów, testy przeciążeniowe mogą weryfikować zachowanie się systemu w sytuacji
strumienia przekraczającego 6000 równoczesnych żądań.
• Testy objętościowe weryfikują zdolność systemu do przetwarzania dużych porcji da-
nych, w kontekście wewnętrznych limitów narzuconych na struktury danych, złożo-
ności obliczeniowej wykorzystywanych algorytmów czy też dużej fragmentacji plików
dyskowych.
• Testy bezpieczeństwa zmierzają do wykrycia luk w zabezpieczeniach systemu. Zna-
nych jest wiele typowych luk bezpieczeństwa i wiele metod ich eksploatowania. Testy
mające na celu ich wykrycie przeprowadzane są zwykle przez „brygady tygrysów"
wykorzystujących całą swą wiedzę i doświadczenie w tej materii.
• Testy uwarunkowań czasowych to próby wymuszenia zachowań systemu naruszających
ograniczenia czasowe wynikające z wymagań pozafunkcyjnych.
530 Rozdział 11. • Testowanie

• Testy odtwarzania oceniają zdolność systemu do wychodzenia z błędnych stanów,


takich jak niedostępność zasobów, awaria sprzętu lub awaria sieci.

Jeżeli żaden z opisanych testów funkcjonalnych i wydajnościowych nie doprowadzi do


awarii systemu, można system ów uznać za przetestowany i zweryfikowany.

Testowanie pilotażowe

Testy pilotażowe, zwane także testami polowymi (field tests), wykonywane są przez
niewielką grupę użytkowników, wykorzystujących system w taki sam sposób, w jak ma on być
eksploatowany w codziennej pracy. Użytkownicy ci nie otrzymują żadnych wytycznych czy
scenariuszy testowania. Takie testy mają kluczowe znaczenie w sytuacji, gdy system tworzony
jest bez wyraźnie określonych wymagań lub dla anonimowych użytkowników. Grupa zaufa-
nych użytkowników testuje wówczas system przez pewien okres czasu, dzieląc się swymi spo-
strzeżeniami z jego autorami.
Testowanie pilotażowe odbywające się w środowisku programistycznym, w którym sys-
tem jest tworzony, nazywane jest powszechnie alfa-testowaniem. Testowanie pilotażowe pro-
wadzone przez użytkowników w ich środowisku eksploatacyjnym nosi nazwę beta-testowania.
Testy pilotażowe tym różnią się od testów użyteczności, iż zachowanie użytkowników testują-
cych system nie jest w żaden sposób rejestrowane ani obserwowane, w rezultacie więc beta-testy
nie są w stanie skutecznie wykrywać problemów z użytecznością systemu. W przypadku inte-
raktywnych systemów, dla których łatwość użytkowania jest jednym z kluczowych wymagań,
beta-testy nie mogą zatem zastąpić testów użyteczności.
Znaczenie beta-testów zwiększyło się znacznie dzięki internetowi, rozwiązującemu
problem dystrybucji testowanego oprogramowania. Istotnie, wiele firm wpisało beta-testy na
stałe w politykę testowania nowych rozwiązań. Ponieważ beta-testerzy sami pobierają testowane
oprogramowanie, beta-testy nie wymagają większej fatygi ze strony producentów tego opro-
gramowania. W związku z tym, do przeszłości należy także problem ograniczonej podaży
beta-testerów: coraz więcej firm oferuje testowanie swych produktów każdemu chętnemu,
który też coraz częściej może trochę zarobić na beta-testowaniu.

Testy akceptacyjne

Ewaluacja systemu przez klienta, w ramach testów akceptacyjnych, może odbywać się
na trzy sposoby. Przystępując do testu wzorcowego (benchmark test), klient przygotowuje ze-
staw przypadków testowych reprezentuj ących typowe warunki funkcjonowania systemu; samo
wykonanie testu może być powierzone użytkownikom systemu lub wyspecjalizowanemu ze-
społowi; ważne jest jednak, by osoby testujące zapoznane zostały z wymaganiami funkcyjnymi
i pozafunkcyjnymi stanowiącymi podstawę testowania.
W przypadku inżynierii wtórnej, czyli opracowywania nowego systemu jako następcy
systemu aktualnie eksploatowanego, przeprowadza się zwykle dwa rodzaje testów porównują-
cych oba systemy. W ramach „testowania rywala" (competitor testing) nowy system testowany
jest w kontekście analogicznych funkcji występujących w istniejącym systemie (i ewentualnie
w podobnych systemach, o ile takie są dostępne do testu). Z kolei „testowanie w cieniu"
11.5. Zarządzanie testowaniem 531

(shadow testing) to równoległe wykonywanie tych samych zadań przez oba systemy i porów-
nywanie otrzymywanych wyników.
W wyniku przeprowadzonych testów akceptacyjnych klient informuje menedżera pro-
jektu (w formie stosownego raportu), które z wymagań stawianych systemowi nie zostały speł-
nione. Testy akceptacyjne stwarzają także okazję do dialogu między klientem a programistami
w sprawie wymagań, które muszą zostać dodane, zmodyfikowane lub anulowane w związku
z zaistniałymi zmianami. Gdy konieczna jest zmiana wymagań, żądania klienta w tym wzglę-
dzie muszą być ujęte w formie protokołu wieńczącego wykonany test akceptacyjny, protokół
ten stanowi bowiem podstawę do następnej iteracji w cyklu rozwojowym systemu.

Testy instalacyjne

Po zaakceptowaniu systemu przez klienta następuje instalowanie tego systemu w środo-


wisku docelowym. Dobry plan testowania powinien definiować łatwą procedurę rekonfiguracji
systemu w związku z przeniesieniem go do tego środowiska z środowiska rozwojowego (czyli
tego, w którym powstawał). Celem testów instalacyjnych jest sprawdzenie, czy system spełnia
wszystkie wymagania w swym nowym środowisku.
W większości przypadków do testów instalacyjnych używa się tych samych przypadków
testowych, które wykorzystywane były do testowania funkcjonalności i wydajności systemu
w jego środowisku rozwojowym. Niektóre wymagania nie mogą być testowane w środowisku
rozwojowym ze względu na brak specyficznych zasobów, zatem konieczne jest opracowanie
nowych przypadków testowych dla zweryfikowania tych właśnie wymagań w środowisku
docelowym.
Gdy wyniki testowania instalacyjnego okażą się satysfakcjonujące dla klienta, testowanie
systemu można uznać za zakończone, zaś system uważa się za formalnie dostarczony klientowi.

11.5. Zarządzanie testowaniem


W poprzednich sekcjach pokazaliśmy, jak stosowanie różnych technik testowania zmierza do
wykrycia jak największej liczby usterek ukrywających się w oprogramowaniu. Obecnie zaj-
miemy się problemem zarządzania aktywnościami realizującymi te techniki, tak by zminimali-
zować zużycie zasobów niezbędnych do ich wykonywania. Większość wspomnianych aktyw-
ności ma miejsce w końcowych fazach projektu, gdy termin dostarczenia systemu zbliża
się nieuchronnie, a dostępne zasoby są już być może na wyczerpaniu. Często dokonuje się
wówczas podziału usterek na te, które muszą zostać usunięte jeszcze przed dostarczeniem
systemu, i te, których naprawienie może dokonać się podczas następnych jego rewizji; tak czy
inaczej programiści powinni wykryć i naprawić wszystkie usterki, które przeszkadzają
w spełnieniu przez system stawianych mu wymagań, w stopniu uniemożliwiającym przyjęcie
tego systemu przez klienta.
Pierwszą czynnością w procesie zarządzania testowaniem jest zaplanowanie poszcze-
gólnych aktywności realizujących testowanie (o czym piszemy w sekcji 11.5.1). Potem nastę-
puje sporządzenie planu testów (patrz sekcja 11.5.2) i przydzielenie ról związanych z testowa-
niem (patrz sekcja 11.5.3). W sekcji 11.5.4 opiszemy także testy regresyjne, sekcję 11.5.5
poświęcimy automatyzacji testowania, a sekcję 11.5.6 — testowaniu bazującemu na modelach.
532 Rozdział 11. • Testowanie

11.5.1. Planowanie testów


Aby zredukować koszty testowania i jego czasochłonność, potrzebne jest staranne zaplanowanie
testów. W tym celu należy przede wszystkim odpowiednio wcześnie przystąpić do tworzenia
przypadków testowych, a także maksymalnie wykorzystać możliwości zrównoleglenia testów.
Programiści odpowiedzialni za testowanie powinni przystąpić do projektowania przy-
padków użycia zaraz po ustabilizowaniu się poszczególnych modeli. Dla testów funkcjonalnych
przypadki testowe mogą być opracowywane zaraz po skompletowaniu przypadków użycia;
testy jednostkowe podsystemów można projektować zaraz po zdefiniowaniu interfejsów;
podobnie sterowniki i namiastki testowe można opracowywać zaraz po zakończeniu tworzenia
kodu odnośnych komponentów. Wczesne zaprojektowanie przypadków testowych umożliwia
wczesne rozpoczęcie samych testów, gdy tylko gotowe będą komponenty podlegające testowa-
niu. Ponadto, ponieważ projektowanie przypadków testowych wymaga gruntownego przeana-
lizowania testowanych modeli, programiści mogą wykrywać usterki tkwiące w tych modelach
jeszcze przed uruchomieniem samych testów. Wczesne opracowywanie przypadków testo-
wych stwarza jednak dodatkową komplikację w postaci utrzymywania tych przypadków,
sterowników i namiastek w synchronizacji ze zmieniającymi się modelami.
Drugim kluczowym czynnikiem redukującym pracochłonność i czasochłonność testów
jest właściwe ich zrównoleglenie — i tak po (zrównoleglonych) testach jednostkowych po-
szczególnych komponentów i usunięciu usterek wykrytych w ramach tych testów przychodzi
kolej na testowanie par komponentów. W przykładowym diagramie PERT8 pokazanym na ry-
sunku 11.19 test czwórki komponentów A-B-C-D może rozpocząć się po zaliczeniu testów
par A-B, A-C i A-D. Każdy z tych testów dwójkowych może natomiast rozpocząć się (nieza-
leżnie od pozostałych dwóch) zaraz po zaliczeniu testów jednostkowych przez komponent A.
Co więcej, czwórka A-B-C-D może być testowana równolegle z parą D-G i trójką B-E-F, nawet
jeśli dwa ostatnie testy wykryją usterki w komponentach E, F lub G, w wyniku czego opóźnione
zostaną następne testy.
W ogólnym bilansie wykorzystywania zasobów przeznaczonych na realizację projektu te-
stowanie przedstawia dość znaczący udział. Zgodnie z zaleceniami metodologii jednolitego
procesu (Unified Process), na testowanie należy przeznaczyć 25% wszystkich zasobów (patrz
sekcja 15.4.2 i książka W. Royca [Royce, 1998]), choć wymogi związane z bezpieczeństwem
i niezawodnością systemu mogą tę wartość znacząco zawyżać. Wczesne planowanie testów
może zmniejszyć ryzyko związane z jej niedoszacowaniem.

11.5.2. Dokumentowanie testowania


Aktywności związane z testowaniem opisywane są w dokumentach czterech różnych ro-
dzajów: w Planie testów, Specyfikacjach przypadków testowych, Raporcie zdarzeń testowych
i Raporcie podsumowującym9.

8
Patrz sekcja 3.3.4 — przyp. tłum.
9
Dla prostoty pominęliśmy inne dokumenty wymienione w standardzie IEEE 829, na którym bazuje
treść niniejszej sekcji, między innymi raport dotyczący przechodzenia komponentów między poszcze-
gólnymi etapami testowania (Test Item Transmittal Report). Uprościliśmy też nieco strukturę nie-
których omawianych dokumentów. Czytelników zainteresowanych szczegółami wspomnianego
standardu odsyłamy do lektury oryginału [IEEE Std. 829-2008].
11.5. Zarządzanie testowaniem 533

Rysunek 11.19. Przykładowy diagram PERT odzwierciedlający harmonogram przeprowadzania testów


„kanapkowych" z rysunku 11.16

• Plan testów koncentruje się na menedżerskich aspektach testowania: jego zakresie,


zastosowanych podejściach, zasobach i harmonogramie. Obejmuje także zestawienie
testowanych wymagań i komponentów podlegających testowaniu.
• Poszczególne przypadki testowe udokumentowane są w Specyfikacjach przypadków
testowych. Specyfikacja przypadku testowego zawiera opis danych wejściowych, ste-
rowników, namiastek, oczekiwanych wyników i (lub) zadań, których wykonania ocze-
kuje się od systemu.
• Skutek zastosowania przypadku testowego udokumentowany jest w towarzyszącym
temu przypadkowi Raporcie zdarzeń testowych, zawierającym wyszczególnienie
wyników testowania i ich rozbieżności z oczekiwaniami.
• Raport podsumowujący zawiera listę wszystkich awarii, jakie wystąpiły w procesie
testowania, i czynności związanych z usunięciem powodujących je usterek. Progra-
miści, analizując ten raport, nadają priorytety poszczególnym awariom i planują ko-
lejność wprowadzania związanych z nimi zmian do modeli i systemu. Mogą wówczas
pojawić się nowe przypadki użycia i konieczność przeprowadzenia nowych testów.

Plan testów i specyfikacje przypadków testowych tworzone są możliwie wcześnie, rów-


nolegle z opracowywaniem poszczególnych przypadków użycia. Dokumenty te muszą pozo-
stawać w synchronizacji ze zmieniającymi się modelami; jako takie podlegają więc zarządzaniu
konfiguracją. Poniżej przedstawiamy szkic typowego planu testów.
534 Rozdział 11. • Testowanie

Plan t e s t ó w

1. Wstęp
2. Powiązanie z innymi dokumentami
3.Ogólna charakterystyka systemu
4. Cechy podlegające i niepodlegające testowaniu
5. Kryteria zaliczenia/niezaliczenia testów
6. Zastosowane podejścia
7. Zawieszanie i wznawianie testów
8. Materiały (sprzęt i oprogramowanie) niezbędne do przeprowadzania testów
9. Przypadki testowe
10. H a r m o n o g r a m testowania

Pierwsza sekcja planu testów opisuje cele testowania i ich rozmiar, w celu wyznaczenia
ram czasowych i kosztowych dla menedżera i programistów planujących testy.
W sekcji drugiej znajdują się odniesienia do innych dokumentów produkowanych
w związku z tworzeniem systemu, takich jak dokument analizy wymagań (RAD), dokument
projektu systemu (SDD) i dokument projektu obiektów (ODD). Odniesienia te specyfikują
powiązanie poszczególnych testów z wymaganiami funkcyjnymi i pozafunkcyjnymi oraz do
szczegółów projektu systemu. W razie potrzeby w sekcji tej zdefiniowane zostają także sche-
maty nazewnictwa odzwierciedlające związki między testami a testowanymi wymaganiami.
Treść sekcji trzeciej odzwierciedla strukturalne aspekty testowania, czyli przegląd syste-
mu w kategoriach komponentów podlegających testowaniu jednostkowemu. Definiowana jest
także ziarnistość komponentów i zależności między nimi.
Sekcja czwarta, koncentrująca się na funkcjonalnych aspektach testowania, wyszczegól-
nia testowane cechy i ich kombinacje. W sekcji tej wymienione są także cechy niepodlegające
testowaniu, wraz ze wskazaniem powodów, dla których rezygnuje się z ich testowania.
W sekcji piątej definiowane są ogólne kryteria kwalifikowania testu jako „zaliczony" albo
„niezaliczony". Zauważmy, że z tej perspektywy „niezaliczenie" testu oznacza test pomyślny
w świetle wcześniej przyjętej przez nas definicji.
Sekcja szósta poświęcona jest ogólnemu opisowi podejścia do procesu testowania. Dys-
kutowane są tu powody wyboru określonych strategii integracyjnych (do testowania integra-
cyjnego różnych części systemów mogą być stosowane odmienne strategie). Zależności między
poszczególnymi testami jednostkowymi oraz związek testów jednostkowych z testami inte-
gracyjnymi odzwierciedlone są na diagramie klas UML
W sekcji siódmej określone są kryteria powodujące zawieszenie testowania poszczegól-
nych komponentów, a także aktywności, które należy powtórzyć przy wznowieniu testowania.
Sekcja ósma wymienia i opisuje zasoby wymagane do testowania: sprzęt, oprogramowa-
nie, specjalne narzędzia i zasoby innych kategorii (na przykład pomieszczenia biurowe).
Sekcja dziewiąta, stanowiąca sedno planu testowania, wymienia wszystkie przypadki te-
stowe wykorzystywane w procesie testowania. Każdy z tych przypadków opisany jest szczegó-
łowo w osobnej Specyfikacji przypadków użycia, a wykonywanie każdego z nich dokumento-
wane jest w Raporcie zdarzeń testowych. Oba te dokumenty opiszemy w dalszej części tej sekcji.
11.5. Zarządzanie testowaniem 535

W sekcji dziesiątej o k r e ś l o n y jest h a r m o n o g r a m t e s t o w a n i a , a t a k ż e z a k r e s o d p o w i e -


dzialności poszczególnych w y k o n a w c ó w testowania, p o t r z e b y k a d r o w e i szkoleniowe w y n i k a -
jące z t e s t o w a n i a o r a z r y z y k o z w i ą z a n e z t e s t o w a n i e m .
Szkic typowej Specyfikacji przypadku testowego (w skrócie TCS, od Test Case Specification)
p r e z e n t u j e się n a s t ę p u j ą c o .

Specyfikacja przypadku testowego

1. Identyfikator przypadku testowego


2. Testowane elementy
3. Specyfikacja danych wejściowych
4. Specyfikacja oczekiwanych wyników
5. Potrzeby środowiskowe
6. Specjalne wymagania proceduralne
7. Zależności od innych przypadków testowych

I d e n t y f i k a t o r p r z y p a d k u t e s t o w e g o jest u n i k a l n ą n a z w ą , o d r ó ż n i a j ą c ą g o o d i n n y c h
p r z y p a d k ó w testowych. K o n w e n c j e zalecające t w o r z e n i e n a z w p r z y p a d k ó w t e s t o w y c h sto-
s o w n i e d o t e s t o w a n y c h k o m p o n e n t ó w l u b ich cech u ł a t w i a j ą p r o g r a m i s t o m o d w o ł y w a n i e się
d o p o s z c z e g ó l n y c h testów. K o m p o n e n t y i (lub) ich c e c h y p o d l e g a j ą c e t e s t o w a n i u p r z y u ż y c i u
d a n e g o p r z y p a d k u testowego w y m i e n i o n e są sekcji d r u g i e j specyfikacji. Sekcja trzecia zawiera
d a n e wejściowe, zaś sekcja czwarta — oczekiwane w y n i k i p r o d u k o w a n e p r z e z system n a p o d -
stawie tych d a n y c h . O c z e k i w a n e w y n i k i m o g ą być obliczane ręcznie albo p r o d u k o w a n e p r z e z
analogiczny system, n a p r z y k ł a d system d o t y c h c z a s e k s p l o a t o w a n y . W sekcji piątej o p i s a n e są
cechy platformy sprzętowej i p r o g r a m o w e j niezbędnej do zastosowania danego przypadku
użycia, a także wszystkie sterowniki i n a m i a s t k i testowe związane z t y m p r z y p a d k i e m . O g r a n i -
czenia w s t o s o w a n i u p r z y p a d k u testowego, n a p r z y k ł a d związane z u w a r u n k o w a n i a m i czaso-
w y m i , obciążeniem serwera czy i n t e r w e n c j ą o p e r a t o r a , o p i s a n e są w sekcji szóstej, sekcja siód-
m a zawiera n a t o m i a s t opis e w e n t u a l n y c h zależności d a n e g o p r z y p a d k u testowego o d i n n y c h .
Raport zdarzeń testowych (TIR, od Test Incident Report) zawiera listę rezultatów otrzy-
m a n y c h w w y n i k u z a s t o s o w a n i a d a n e g o p r z y p a d k u użycia i w y k a z e w e n t u a l n y c h awarii, jakie
wystąpiły w związku z t e s t o w a n i e m . O p i s w y n i k ó w m u s i zawierać wyszczególnienie testowa-
n y c h cech i wyjaśnienie, dlaczego t e s t o w a n i e d a n e j c e c h y rozstrzygnięte zostało p o z y t y w n i e .
O p i s o w i każdej awarii towarzyszyć m u s i i n f o r m a c j a wystarczająca d o r e p r o d u k o w a n i a tej awarii
n a żądanie 1 0 . Awarie zaistniałe w w y n i k u stosowania wszystkich p r z y p a d k ó w testowych zebra-
n e są w Raporcie sumarycznym, k t ó r y dla p r o g r a m i s t ó w s t a n o w i p o d s t a w ę d o p o s z u k i w a n i a
i naprawiania odnośnych usterek, zgodnie z ustalonymi priorytetami.
D l a p o r z ą d k u m u s i m y w t y m m i e j s c u w s p o m n i e ć , że s t a n d a r d [IEEE Std. 8 2 9 - 2 0 0 8 ]
d o k u m e n t o w a n i a o p r o g r a m o w a n i a zaleca t r o c h ę i n n ą s t r u k t u r ę poszczególnych d o k u m e n t ó w ,
szczególnie p r z y d a t n ą dla o g r o m n y c h organizacji i wysoce s k o m p l i k o w a n y c h s y s t e m ó w

10
Znaczenie powtarzalności niepożądanych zachowań systemu dla efektywności testowania wyjaśnione
jest obszernie w książce P. Butchera Debugowanie. Jak wyszukiwać i naprawiać błędy w kodzie oraz im
zapobiegać, wyd. Helion 2010, http://helion.pI/ksiazki/debugo.htm — przyp. tłum.
536 Rozdział 11. • Testowanie

— m i ę d z y i n n y m i z a m i a s t s e k c j i Planu testów opatrzonej przez nas n u m e r e m dziesiątym


w y s t ę p u j e kilka o s o b n y c h sekcji p o ś w i ę c o n y c h o d p o w i e d z i a l n o ś c i p r o g r a m i s t ó w , p o t r z e b o m
k a d r o w y m i szkoleniowym, harmonogramowi, ryzyku i ewentualnym uwarunkowaniom
specjalnym i wariantom testowania.

11.5.3. Przydzielanie odpowiedzialności


Testowanie wymaga od testerów efektywnego wykrywania usterek tkwiących w oprogramo-
w a n i u , n a t y m b o w i e m z a s a d z a się j e g o sens. Jest oczywiste, iż w u z y s k a n i u tej e f e k t y w n o ś c i
n a p e w n o nie p o m a g a niechęć d o załamywania pracy testowanych k o m p o n e n t ó w , stąd testo-
wanie określonego k o m p o n e n t u p o w i n n o być w y k o n y w a n e przez osobę, która nie uczest-
niczyła w jego tworzeniu. S a m a m o t y w a c j a d o szukania przysłowiowej „dziury w całym" nie
jest j e d n a k w y s t a r c z a j ą c a , k o n i e c z n a jest t a k ż e o d p o w i e d n i a w i e d z a i i n t u i c j a p o m o c n a w w y -
krywaniu niejasności tkwiących w samej specyfikacji k o m p o n e n t u .
W p r z y p a d k u szczególnie r e s t r y k c y j n y c h w y m a g a ń p o d a d r e s e m s y s t e m u j e g o t e s t o w a -
n i e p o w i e r z a n e jest s p e c j a l n e m u z e s p o ł o w i o d p o w i e d z i a l n e m u za k o n t r o l ę jakości. Z e s p ó ł t e n
o t r z y m u j e wszystkie m o d e l e s y s t e m u , j e g o k o d ź r ó d ł o w y i s k o m p i l o w a n e b i n a r i a , n a p o d s t a w i e
których o p r a c o w u j e i w y k o n u j e przypadki testowe. Po p r z e p r o w a d z e n i u testów zespół kontroli
j a k o ś c i p r z e k a z u j e z e s p o ł o m o d p o w i e d z i a l n y m za p o s z c z e g ó l n e p o d s y s t e m y r e z u l t a t y t e s t o w a -
nia, w p o s t a c i d o k u m e n t ó w TIR i Raportu sumarycznego, w celu i c h analizy i d o k o n a n i a z m i a n
w k o l e j n e j iteracji systemu. P o p r a w i o n y system jest n a s t ę p n i e p o n o w n i e t e s t o w a n y p r z e z
z e s p ó ł k o n t r o l i jakości, p r z y c z y m n i e t y l k o w e r y f i k o w a n e jest u s u n i ę c i e z a u w a ż o n y c h u p r z e d -
n i o u s t e r e k , lecz t a k ż e n a s t ę p u j e p r ó b a w y k r y c i a u s t e r e k , k t ó r e m o g ł y zostać d o s y s t e m u w p r o -
wadzone w efekcie nanoszenia p o p r a w e k .
W przypadku systemów o mniej rygorystycznych w y m o g a c h testowanie danego pod-
systemu m o ż e być wykonywane przez zespół programistów pracujących n a d i n n y m p o d -
systemem. Zespół architektoniczny pełni wówczas rolę koordynacyjną, definiując standardy
dla p r o c e d u r testowych, s t e r o w n i k ó w i namiastek. K o m u n i k a c j a m i ę d z y poszczególnymi
z e s p o ł a m i o p i e r a się n a t y c h s a m y c h d o k u m e n t a c h , c o p o p r z e d n i o w y m i e n i o n e .
J e d n y m z g ł ó w n y c h p r o b l e m ó w t o w a r z y s z ą c y c h t e s t o m u ż y t e c z n o ś c i jest w e r b o w a n i e
u ż y t k o w n i k ó w p r z e p r o w a d z a j ą c y c h takie testy: m e n e d ż e r o w i e p r o j e k t ó w b o r y k a j ą się w ó w c z a s
z r ó ż n y m i przeszkodami, jak również z przejawami własnej niechęci, między innymi
[ G r u d i n , 1990]:

• M e n e d ż e r o w i e p r o j e k t ó w o b a w i a j ą się, że u ż y t k o w n i c y t e s t u j ą c y s y s t e m o m i j a ć b ę d ą
u s t a n o w i o n ą o r g a n i z a c j ę k o m u n i k a c y j n ą , k o n t a k t u j ą c się b e z p o ś r e d n i o z p r o g r a m i -
stami, co m o ż e wpływać destrukcyjnie n a wykonywanie codziennych zadań powie-
rzonych tym programistom;

• Dział s p r z e d a ż y m o ż e żywić o b a w y , że b e z p o ś r e d n i e k o n t a k t y „jego" k l i e n t ó w z p r o -


gramistami m o g ą wywrzeć n a tych klientach niekorzystne wrażenie bądź naruszyć
z a u f a n i e d o a k t u a l n i e o f e r o w a n y c h p r o d u k t ó w ( k t ó r e w c i ą ż m u s z ą się s p r z e d a w a ć ) ;

• Użytkownicy m o g ą p o prostu nie dysponować w o l n y m czasem;

• Użytkownicy m o g ą wykazywać niechęć d o n o w e g o systemu, n a przykład żywiąc


o b a w y , że j e g o r o z s z e r z o n a f u n k c j o n a l n o ś ć m o ż e p o z b a w i ć i c h p r a c y .
11.5. Zarządzanie testowaniem 537

N a l e ż y p a m i ę t a ć , że w p r z y p a d k u t e s t o w a n i a u ż y t e c z n o ś c i s y s t e m u liczy się p r z e d e
w s z y s t k i m z d a n i e u ż y t k o w n i k ó w d o k o n u j ą c y c h t e g o t e s t o w a n i a . Jeśli w i ę c t e s t o w a n i e t o s t a n i e
się ś w i a d e c t w e m w y r a ź n y c h p r o b l e m ó w , k o n i e c z n e jest w y s ł u c h a n i e o p i n i i t e s t e r ó w , d l a c z e g o
(ich z d a n i e m ) p r o b l e m y te w ogóle wystąpiły. R e k o m e n d a c j e d o t y c z ą c e u s p r a w n i e n i a t e s t o w a -
nych k o m p o n e n t ó w p o w i n n y zostać przekazane p r o g r a m i s t o m jak najszybciej p o zakończe-
niu testów.

11.5.4. Testowanie regresyjne


T w o r z e n i e o p r o g r a m o w a n i a z o r i e n t o w a n e g o o b i e k t o w o jest p r o c e s e m i t e r a c y j n y m , w i ą ż ą c y m
się z w i e l o k r o t n y m m o d y f i k o w a n i e m , t e s t o w a n i e m i i n t e g r o w a n i e m p o s z c z e g ó l n y c h k o m p o -
n e n t ó w . Z m o d y f i k o w a n y k o m p o n e n t jest, oczywiście, p o d d a w a n y t e s t o m j e d n o s t k o w y m
— t y m s a m y m , co p o p r z e d n i o , p l u s e w e n t u a l n i e n o w y m , z w i ą z a n y m z n o w y m i a s p e k t a m i j e g o
f u n k c j o n a l n o ś c i . Z a l i c z e n i e t y c h t e s t ó w j e s t s i l n ą p r z e s ł a n k ą n a r z e c z tezy, że m o d y f i k a c j a
k o m p o n e n t u n i e s p o w o d o w a ł a w p r o w a d z e n i a d o ń n o w y c h u s t e r e k . N i e o z n a c z a t o j e d n a k , że
u s t e r k i t a k i e n i e m o g ą p o j a w i ć się w w y n i k u w s p ó ł p r a c y z m o d y f i k o w a n e g o k o m p o n e n t u
z „ r e s z t ą " s y s t e m u , n a w e t jeżeli t e n zaliczył u p r z e d n i o w s z y s t k i e w y k o n y w a n e testy. O p r o -
g r a m o w a n i e jest t w o r e m s u b t e l n y m i m o d y f i k a c j a w r a m a c h j e d n e g o k o m p o n e n t u m o ż e
s t w o r z y ć w a r u n k i s p r z y j a j ą c e u j a w n i e n i u się u s t e r e k w i n n y c h m i e j s c a c h s y s t e m u — u s t e r e k ,
które pozostawały dotąd niewykryte, m i m o niespożytej inwencji i wysiłku testerów. W o b e c
takiej p e r s p e k t y w y c e l o w e o k a z u j e się p o n o w n e p r z e p r o w a d z e n i e w s z y s t k i c h t e s t ó w i n t e g r a -
c y j n y c h — n a z y w a n e są o n e w ó w c z a s t e s t a m i r e g r e s y j n y m i .
Najbardziej w y g o d n y m i niezawodnym sposobem przeprowadzania testów regresyjnych
jest i c h z a k u m u l o w a n i e i u r u c h a m i a n i e k a ż d o r a z o w o , g d y n o w y l u b z m o d y f i k o w a n y k o m p o -
n e n t i n t e g r o w a n y jest z resztą systemu. W y m a g a to o d p r o g r a m i s t ó w u t r z y m y w a n i a a k t u -
alnego stanu p r z y p a d k ó w testowych związanych z takimi testami, stosownie d o zmieniających
się i n t e r f e j s ó w p o s z c z e g ó l n y c h klas i p o d s y s t e m ó w . P o n i e w a ż j e d n a k k o m p l e t n e testy r e g r e s y j n e
m o g ą być niedopuszczalnie czasochłonne, o p r a c o w a n o kilka technik ich p r z e p r o w a d z a n i a
w s p o s ó b w y b i ó r c z y . T e c h n i k i te b a z u j ą n a szczególnie n e w r a l g i c z n y c h e l e m e n t a c h s y s t e m u ,
k t ó r y m i są m i ę d z y i n n y m i [ B i n d e r , 2 0 0 0 ] :

• komponenty zależne od zmodyfikowanego komponentu. Są one najbardziej prawdo-


p o d o b n y m i k a n d y d a t a m i d o obnażenia swych usterek w r a m a c h testów regresyjnych.
Zyskujemy t y m s a m y m maksymalizację p r a w d o p o d o b i e ń s t w a sukcesu w obliczu na-
rzuconych ograniczeń, wykluczających opłacalność kompletnych testów regresyjnych.

• szczególnie ryzykowne przypadki użycia. Zidentyfikowanie jednej lub kilku katastro-


ficznych usterek jest zwykle dla s y s t e m u b a r d z i e j p o ż y t e c z n e n i ż z i d e n t y f i k o w a n i e d u ż e j
n a w e t liczby u s t e r e k o p o m n i e j s z y m z n a c z e n i u . K o n c e n t r u j ą c się n a r y z y k o w n y c h
p r z y p a d k a c h użycia, zwiększamy swe szanse w p o s z u k i w a n i u tych pierwszych.

• często wykorzystywane przypadki użycia. Gdy użytkownicy otrzymują nową wersję


s y s t e m u , m a j ą p r a w o oczekiwać, iż f u n k c j e r e a l i z o w a n e b e z b ł ę d n i e d o tej p o r y n a d a l
będą realizowane bez zarzutu. A b y więc zminimalizować ryzyko „zepsucia" systemu
w n o w e j wersji, p r o g r a m i ś c i p o w i n n i s k o n c e n t r o w a ć się w p i e r w s z y c h rzędzie n a t y c h
p r z y p a d k a c h użycia, k t ó r e u ż y t k o w n i c y s y s t e m u realizują najczęściej.
538 Rozdział 11. • Testowanie

T e s t o w a n i e r e g r e s y j n e z a w s z e w i ą ż e się z w i e l o k r o t n y m w y k o n y w a n i e m d u ż e j l i c z b y
t e s t ó w . Jest w i ę c o p ł a c a l n e t y l k o w ó w c z a s , jeśli d a j e się w d u ż y m s t o p n i u a u t o m a t y z o w a ć p o d
względem konfigurowania, uruchamiania i weryfikowania wyników.

11.5.5. Automatyzacja testowania


Testowanie „ręczne" wymaga o d testerów pracochłonnego wprowadzania predefiniowanych
d a n y c h d o s y s t e m u za p o ś r e d n i c t w e m i n t e r f e j s u G U I , k o n s o l i p o l e c e ń , d e b u g g e r a i t y m p o -
dobnych, a po wykonaniu testów — wzrokowego porównywania otrzymanych wyników
z o c z e k i w a n y m i , p r e d e f i n i o w a n y m i p r z e z w y r o c z n i ę . N i e jest n i e s p o d z i a n k ą , iż są t o c z y n n o ś c i
n u ż ą c e i w y s o c e p o d a t n e n a błędy, szczególnie p r z y d u ż e j liczbie t e s t ó w i (lub) d u ż e j o b j ę t o ś c i
w y n i k ó w p r o d u k o w a n y c h p r z e z t e s t o w a n e p o d s y s t e m y . G d y z m i e n i a j ą się w y m a g a n i a i s y s t e m
g w a ł t o w n i e e w o l u u j e , k o n i e c z n a jest a b s o l u t n a p o w t a r z a l n o ś ć w s z y s t k i c h t e s t ó w . W p r z y p a d k u
ręcznego testowania zapewnienie takiej powtarzalności m o ż e być praktycznie niemożliwe.
M o ż n a j ą j e d n a k b a r d z o ł a t w o u z y s k a ć dzięki a u t o m a t y z a c j i t e s t o w a n i a . M i m o iż a u t o -
matyzować m o ż n a wszelkie aspekty testowania (między i n n y m i generowanie p r z y p a d k ó w
testowych i oczekiwanych wyników), szczególnego znaczenia nabiera automatyzacja wykony-
w a n i a t e s t ó w . D l a t e s t o w a n i a s y s t e m u p r z y p a d k i t e s t o w e w y r a ż a n e są w k a t e g o r i a c h o k r e -
ś l o n y c h d a n y c h w e j ś c i o w y c h i (lub) u w a r u n k o w a ń c z a s o w y c h o r a z s p o d z i e w a n y c h w y n i k ó w ;
ś r o d o w i s k o t e s t o w e ( z w a n e p o p u l a r n i e „ u p r z ę ż ą t e s t o w ą " — test harness) jest w ó w c z a s o d p o -
wiedzialne za u r u c h a m i a n i e k o l e j n y c h p r z y p a d k ó w t e s t o w y c h i k o n f r o n t o w a n i e o t r z y m y w a n y c h
w y n i k ó w z zapisami wyroczni. W testach jednostkowych programiści specyfikują odpowiedni
sterownik testowy, automatycznie wywołujący operacje testowanego k o m p o n e n t u .
B e z p o ś r e d n i ą k o r z y ś c i ą p ł y n ą c ą z a u t o m a t y z o w a n i a t e s t ó w jest g w a r a n t o w a n a p o w t a -
rzalność. G d y p o p r a w i o n a z o s t a n i e u s t e r k a b ę d ą c a p r z y c z y n ą z a i s t n i a ł e j a w a r i i , t e s t y , k t ó r e
d o p r o w a d z i ł y d o tej awarii, zostają p o w t ó r z o n e w celu z a p e w n i e n i a , że sytuacja się n i e p o w t ó r z y .
P o n a d t o m o g ą b y ć u r u c h a m i a n e n o w e t e s t y d a j ą c e ( o g r a n i c z o n ą ) p e w n o ś ć , że d o k o n a n e p o -
p r a w k i n i e stały się ź r ó d ł e m n o w y c h u s t e r e k . G d y takie testy u r u c h a m i a n e są w i e l o k r o t n i e , n a
p r z y k ł a d w z w i ą z k u z r e f a k t o r y z o w a n i e m k o d u ( p a t r z sekcja 10.3.2), ich k o s z t z m n i e j s z a się
b a r d z o w y r a ź n i e . N a l e ż y j e d n a k m i e ć n a u w a d z e fakt, że t w o r z e n i e „ u p r z ę ż y t e s t o w e j " jest cza-
s o c h ł o n n ą inwestycją, opłacalną tylko w perspektywie wielokrotnego w y k o n y w a n i a da-
n y c h testów. G d y m a j ą być u r u c h a m i a n e raz czy dwa, t e s t o w a n i e r ę c z n e jest bardziej roz-
sądną alternatywą.
Przykładem zautomatyzowanej infrastruktury testowej m o ż e być f r a m e w o r k JUnit,
w s p o m a g a j ą c y t e s t o w a n i e klas języka Java [JUnit, 2009]. F r a m e w o r k t e n s k ł a d a się z niewielkiej
liczby ściśle z i n t e g r o w a n y c h klas ( p a t r z r y s u n e k 11.20). T w o r z e n i e n o w y c h p r z y p a d k ó w testo-
w y c h s p r o w a d z a się d o d e f i n i o w a n i a subklas klasy T e s t C a s e . M e t o d y ( p r z e d e f i n i o w a n e w s u b -
klasie) s e t U p O i tearDown () o d p o w i e d z i a l n e są za i n i c j o w a n i e ś r o d o w i s k a t e s t o w e g o w z w i ą z k u
z d a n y m p r z y p a d k i e m t e s t o w y m o r a z czyszczenie ś r o d o w i s k a p o w y k o n a n i u t e g o p r z y p a d k u ;
właściwa realizacja p r z y p a d k u testowego — w t y m k o n f r o n t o w a n i e o t r z y m a n y c h w y n i k ó w
z w y r o c z n i ą — o d b y w a się w r a m a c h ( p r z e d e f i n i o w a n e j ) m e t o d y r u n T e s t ( ) . W e r d y k t — zali-
czenie a l b o z a ł a m a n i e t e s t u — r e p r e z e n t o w a n y jest p r z e z i n s t a n c j ę klasy T e s t R e s u l t . P r z y p a d k i
t e s t o w e o r g a n i z o w a n e są zwykle w zestawy, r e p r e z e n t o w a n e p r z e z klasę T e s t S u i t e ( ) , o d p o -
wiedzialną za s e k w e n c y j n e u r u c h a m i a n i e p o d p o r z ą d k o w a n y c h p r z y p a d k ó w testowych.
11.5. Zarządzanie testowaniem 539

Rysunek 11.20. Struktura frameworku JUni t

Zazwyczaj j e d n a instancja klasy T e s t C a s e w y w o ł u j e p o j e d y n c z ą m e t o d ę t e s t o w a n e j klasy.


A b y j e d n a k u n i k n ą ć n a d m i e r n e j proliferacji subklas, t e s t o w a n i e wszystkich m e t o d d a n e j klasy,
wymagające tego samego sposobu inicjowania środowiska testowego, z g r u p o w a n e zostaje
w j e d n e j subklasie ( C o n c r e t e T e s t C a s e n a r y s u n k u 11.20), c h o ć k a ż d a z m e t o d t e s t o w a n a jest
p r z y u ż y c i u o d r ę b n e j i n s t a n c j i tej klasy. U m o ż l i w i a t o p r o g r a m i s t o m ł a t w e o r g a n i z o w a n i e
i selektywne wywoływanie obszernych zestawów testowych.

11.5.6. Testowanie bazujące na modelach


T e s t o w a n i e (ręczne lub z a u t o m a t y z o w a n e ) w y m a g a i n f r a s t r u k t u r y d o p r z e p r o w a d z a n i a testów,
instrumentacji testowanego systemu oraz kolekcjonowania i oceniania wyników. Infrastruk-
tura ta nazywana jest środowiskiem testowym lub (bardziej obrazowo) uprzężą testową
(test harness); składa się o n a z k o m p o n e n t ó w s p r z ę t o w y c h i p r o g r a m o w y c h , współdziałających
z r ó ż n y m i a k t o r a m i — w s p ó ł d z i a ł a n i e t o m o ż e b y ć m o d e l o w a n e za p o m o c ą j ę z y k a U M L .
W r o z d z i a ł a c h o d 2. d o 10. w i d z i e l i ś m y liczne z a s t o s o w a n i a j ę z y k a U M L d o m o d e l o w a n i a
n a j r o z m a i t s z y c h a s p e k t ó w b u d o w a n e g o systemu; w p o d o b n y s p o s ó b za p o m o c ą U M L m o d e -
lować m o ż n a w s p o m n i a n e ś r o d o w i s k o t e s t o w e — b y j e d n a k b y ł o t o m o ż l i w e , k o n i e c z n e
jest r o z s z e r z e n i e U M L o n o w e o b i e k t y encji.
J e d n y m z ś r o d k ó w z a r z ą d z a n i a języka U M L są profile. W o g ó l n y m p r z y p a d k u p r o f i l
U M L to kolekcja n o w y c h stereotypów, n o w y c h interfejsów i n o w y c h ograniczeń, r e p r e z e n t u j ą c a
specjalizowane koncepcje z zakresu dziedziny aplikacyjnej lub dziedziny realizacyjnej.
P r z y k ł a d e m takiego profilu, w y k o r z y s t y w a n e g o n a p o t r z e b y m o d e l o w a n i a testów, jest
U M L 2 T e s t i n g Profile, w skrócie U 2 T P [ O M G , 2005]. M o d e l o w a n i e ś r o d o w i s k a testowego za
p o m o c ą U 2 T P m a te s a m e zalety, co m o d e l o w a n i e b u d o w a n e g o systemu: dostarcza s t a n d a r d o w ą
540 Rozdział 11. • Testowanie

notację zrozumiałą przez wszystkich uczestników, umożliwia automatyczne generowanie


przypadków testowych na podstawie modelu oraz zapewnia automatyczne wykonywanie
testów i kolekcjonowanie ich wyników.
Profil U 2 T P rozszerza język U M L o następujące elementy:

• Stereotyp «sut» reprezentujący testowany system (system under test), którym może
b y ć k o m p l e t n y s y s t e m l u b tylko jego część — p o d s y s t e m albo p o j e d y n c z a klasa.

• Stereotyp « t e s t C a s e » reprezentujący przypadek testowy, stanowiący specyfikację


z a c h o w a n i a r e a l i z u j ą c e g o j e d e n l u b więcej c e l ó w p r o j e k t o w y c h . P r z y p a d e k t e s t o w y
definiuje sekwencję interakcji m i ę d z y t e s t o w a n y m s y s t e m e m a k o m p o n e n t a m i śro-
d o w i s k a t e s t o w e g o ; i n t e r a k c j e te m o g ą m i e ć p o s t a ć b o d ź c ó w w y s y ł a n y c h d o t e s t o w a -
n e g o s y s t e m u albo o b s e r w o w a n y c h z a c h o w a ń t e s t o w a n e g o s y s t e m u l u b k o m p o n e n t ó w
ś r o d o w i s k a t e s t o w e g o . W j ę z y k u U M L p r z y p a d e k t e s t o w y r e p r e z e n t o w a n y jest p r z e z
d i a g r a m s e k w e n c j i l u b d i a g r a m s t a n ó w . W y n i k i e m p r z y p a d k u t e s t o w e g o jest w a r t o ś ć
t y p u w y l i c z e n i o w e g o verdict, o k r e ś l a j ą c a j e d e n z c z t e r e c h w a r i a n t ó w z a k o ń c z e n i a
testu: zaliczenie ( p a s s e d ) , z a ł a m a n i e ( f a i l e d ) , b r a k r o z s t r z y g n i ę c i a ( i n c o n c l u s i v e )
i błąd ( e r r o r ) . W terminologii U 2 T P „błąd" oznacza błędne funkcjonowanie samego
środowiska testowego, podczas gdy „załamanie" to niezaliczenie testu.

• S t e r e o t y p « t e s t O b j e c t i ve» r e p r e z e n t u j ą c y cel t e s t o w a n i a w p o s t a c i n i e f o r m a l -
n e g o o p i s u ( w j ę z y k u p o t o c z n y m ) z w i ą z a n e g o z j e d n y m l u b k i l k o m a p r z y p a d k a m i te-
s t o w y m i . C e l e m t y m jest zazwyczaj w y m a g a n i e (lub j e g o w y b r a n y aspekt) p o d l e g a j ą c e
weryfikacji. P r z y k ł a d o w o p r z y p a d e k t e s t o w y di s p l a y T i c k e t P r i c e s m a za z a d a n i e
z w e r y f i k o w a n i e p o p r a w n o ś c i w y ś w i e t l a n i a c e n b i l e t ó w p r z e z s p r z e d a j ą c y je a u t o m a t
p r z y z a ł o ż e n i u , że p o d r ó ż n y m o ż e d o w o l n i e o p e r o w a ć p r z y c i s k a m i o z n a c z a j ą c y m i
strefy.

• Stereotyp «testComponent» reprezentujący k o m p o n e n t testowy, n a przykład na-


m i a s t k ę t e s t o w ą l u b p r o g r a m p o m o c n i c z y n i e z b ę d n y d o realizacji p r z y p a d k u użycia.
P r z y k ł a d e m t a k i e g o k o m p o n e n t u jest s y m u l o w a n e u r z ą d z e n i e , s y m u l o w a n e z a c h o -
wanie użytkownika lub k o m p o n e n t y udające awarie.

• Stereotyp « t e s t C o n t e x t » reprezentujący kontekst testowania w postaci zbioru


p r z y p a d k ó w testowych, konfiguracji k o m p o n e n t ó w testowych, kontrolera zarzą-
dzającego sekwencyjnym u r u c h a m i a n i e m p r z y p a d k ó w użycia i t y m p o d o b n e .

• I n t e r f e j s Arb i t e r r e p r e z e n t u j ą c y arbitra, czyli m e c h a n i z m z b i e r a j ą c y r e z u l t a t y lokal-


nych testów w zagregowany wynik.

• I n t e r f e j s S c h e d u l e r , r e p r e z e n t u j ą c y z a r z ą d c ę t e s t o w a n i a , czyli m e c h a n i z m od-
p o w i e d z i a l n y za t w o r z e n i e i w y k o n y w a n i e k o l e j n y c h p r z y p a d k ó w t e s t o w y c h .

N a r y s u n k u 11.21 w i d o c z n y jest d i a g r a m U 2 T P o d z w i e r c i e d l a j ą c y ś r o d o w i s k o t e s t o w e
s y s t e m u T i c k e t D i s t r i b u t o r p r z e d s t a w i a n e g o n a r y s u n k u l ł . 1 8 o r a z w t a b e l a c h 11.8 i 11.9.
K o n t e k s t t e s t o w y P u r c h a s e T i c k e t S u i t e g r u p u j e wszystkie p r z y p a d k i t e s t o w e z w i ą z a n e z p r z y -
p a d k i e m użycia P u r c h a s e T i c k e t . T e s t o w a n y m s y s t e m e m jest o p r o g r a m o w a n i e a u t o m a t u d o
s p r z e d a ż y b i l e t ó w Ti c k e t D i s t r i b u t o r . A b y u ł a t w i ć s o b i e s t e r o w a n i e t y m s y s t e m e m i j e g o
i n s t r u m e n t a c j ę , s y m u l u j e m y wyświetlacz w s p o m n i a n e g o a u t o m a t u za p o m o c ą k o m p o n e n t u
DisplaySimulator.
10.7. Literatura uzupełniająca 541

Rysunek 11.21. Przykład środowiska testowego dla systemu Ti cketDi s t r i butor

I tak n a r y s u n k u 11.22 przedstawione zostały spodziewane interakcje w r a m a c h p r z y p a d k u


t e s t o w e g o di s p l ayTi c k e t P r i c e s , p r o w a d z ą c e d o z a l i c z e n i a t e s t u (czyli w e r d y k t u p a s s ) ,
s e l e c t Z o n e l O , s e l e c t Z o n e 2 ( ) i s e l e c t Z o n e 4 ( ) t o b o d ź c e wysyłane d o t e s t o w a n e g o systemu,
zaś g e t D i s p l a y ( ) to obserwacje umożliwiające o c e n ę p o p r a w n o ś c i poszczególnych k r o k ó w .
Z a u w a ż m y , że r e p r e z e n t o w a n e są tylko oczekiwane interakcje; wszelkie interakcje nieoczeki-
w a n e l u b nieistniejące o r a z obserwacje n i e p o t w i e r d z a j ą c e oczekiwań p r o w a d z ą d o w e r d y k t u
f a i 1 ed. U 2 T P u d o s t ę p n i a także m e c h a n i z m y ( n i e o p i s y w a n e t u t a j ) d o j a w n e g o m o d e l o w a n i a
i n t e r a k c j i p r o w a d z ą c y c h d o w e r d y k t ó w f a i 1 ed i i n c o n c l us i ve.
P r z y p a d e k t e s t o w y di s p l ayTi c k e t P r i c e s z r y s u n k u 11.22 w s p o s ó b j a w n y m o d e l u j e
o d w z o r o w a n i e stref n a c e n y b i l e t ó w . W t y p o w y m s y s t e m i e t a k i e o d w z o r o w a n i e m o ż e b y ć
n i e w y k o n a l n e , b o w i e m wiele p r z y p a d k ó w testowych p o w t a r z a n y c h jest dla tych s a m y c h w a r t o -
ści g r a n i c z n y c h i dla r e p r e z e n t a n t ó w r ó ż n y c h klas r ó w n o w a ż n o ś c i . U 2 T P u m o ż l i w i a r a d z e n i e
sobie z t y m p r o b l e m e m , b o dostarcza koncepcję puli d a n y c h (DataPool), partycji d a n y c h
( D a t a P a r t i t i o n ) i selektora d a n y c h ( D a t a S e l e c t o r ) odzwierciedlających ( o d p o w i e d n i o ) przy-
kładowe dane, klasę r ó w n o w a ż n o ś c i i strategię w y b o r u reprezentanta dla tej klasy. D a j e t o możli-
wość p a r a m e t r y z o w a n i a p r z y p a d k ó w testowych przez r ó ż n e zbiory wartości i w konsekwencji za-
c h o w a n i e zwięzłości specyfikacji t e s t ó w o r a z w i e l o k r o t n e w y k o r z y s t y w a n i e tej specyfikacji.

11.6. Literatura uzupełniająca


H i s t o r y c z n i e t e r m i n „ p l u s k w a " p o r a z p i e r w s z y p o j a w i ł się w i n f o r m a t y c e za s p r a w ą a d m .
Grace H o p p e r z U S Navy, k t ó r a w latach 40. ubiegłego w i e k u znalazła m a r t w ą ć m ę zakleszczoną
m i ę d z y s t y k a m i p r z e k a ź n i k a w k o m p u t e r z e H a r v a r d M a r k II. O p e r a t o r z y załączyli m a r t w ą
ć m ę d o d z i e n n i k a eksploatacji s y s t e m u z adnotacją, że właśnie „odpluskwili" k o m p u t e r . T e r -
m i n t e n u p o w s z e c h n i ł się rychło w z n a c z e n i u u s u w a n i a b ł ę d ó w z o p r o g r a m o w a n i a , co zostało
o p i s a n e p r z e z G. M . H o p p e r [ H o p p e r , 1981].
Jak u d o w a d n i a M . E. F a g a n w swej publikacji [Fagan, 1976] n a p o d s t a w i e licznych ekspe-
rymentów, inspekcja k o d u m o ż e być bardziej efektywna niż trwające tak s a m o długo czar-
noskrzynkowe wyszukiwanie usterek. W związku z tym, inspekcje k o d u i jego przeglądy
p a r t n e r s k i e stanowią n i e o d ł ą c z n ą część kilku s t a n d a r d ó w t w o r z e n i a o p r o g r a m o w a n i a , m i ę d z y
542 Rozdział 11. • Testowanie

Rysunek 11.22. Przykładowy przebieg testu dla przypadku testowego di spl ayTi cketPri ces (). Widoczne
są jedynie interakcje prowadzące do werdyktu pass

i n n y m i s t a n d a r d u I S O 9000. T a k się j e d n a k s k ł a d a , że i n s p e k c j a k o d u n i e jest p o w s z e c h n i e


p r a k t y k o w a n a , z w y j ą t k i e m być m o ż e k r y t y c z n y c h systemów, b o w i e m — jak n a ironię — p o -
strzegana jest j a k o t e c h n i k a wielce c z a s o c h ł o n n a .
N a t e m a t t e s t o w a n i a o p r o g r a m o w a n i a n a p i s a n o j u ż m n ó s t w o k s i ą ż e k . P o s t ę p y w tej
dyscyplinie są j e d n a k s t o s u n k o w o p o w o l n e i tylko niewiele n o w y c h p o m y s ł ó w m o ż e być p o -
r ó w n y w a l n y c h z t e c h n i k a m i inspekcji k o d u źródłowego. Książka G. J. M y e r s a [Myers, 1979],
m i m o iż d o ś ć wiekowa, n a d a l s t a n o w i w a ż n ą pozycję klasyki p r o g r a m i s t y c z n e j , a jej treść p o z o -
staje a k t u a l n a w o d n i e s i e n i u d o w s p ó ł c z e s n y c h s y s t e m ó w .
P o j a w i e n i e się o b i e k t o w y c h t e c h n i k p r o g r a m o w a n i a o t w a r ł o d r o g ę d o p o s t ę p u j ą c e j
m o d u l a r y z a c j i o p r o g r a m o w a n i a i w i e l o k r o t n e g o w y k o r z y s t y w a n i a k o d u . P o l i m o r f i z m okazał
się j e d n a k n a r z ę d z i e m tyleż p o t ę ż n y m , co k ł o p o t l i w y m w t e s t o w a n i u , ze w z g l ę d u n a koniecz-
n o ś ć pokrycia dużej liczby ścieżek przepływu sterowania. W książce R. V. Bindera [Binder, 2000]
znaleźć m o ż n a w y j ą t k o w o obszerne o m ó w i e n i e t e c h n i k i p r o b l e m ó w związanych z t e s t o w a n i e m
p r o g r a m ó w zorientowanych obiektowo.
P a n u j ą c e p o w s z e c h n i e b ł ę d n e p r z e k o n a n i e , j a k o b y t e s t o w a n i e użyteczności w y m a g a ł o
o g r o m n e g o b u d ż e t u i z a a w a n s o w a n e j wiedzy, obalają a u t o r z y książek, tacy jak J. R u b i n [ R u b i n ,
1994] oraz J. Nielden i R. L. M a c k [Nielsen i M a c k , 1994], dostarczając praktycznych p r z y k ł a d ó w
11.7. Ćwiczenia 543

świadczących o tym, iż nawet ograniczone testy użyteczności mogą w drastyczny sposób przy-
czynić się do usprawnienia systemu. W książce autorstwa D. J. Mayhewa [Mayhew, 1999] opi-
sana jest integracja testowania użyteczności z ogólnym cyklem życia oprogramowania zo-
rientowanego obiektowo.
Tworzenie niezawodnych systemów wykracza daleko poza tylko testowanie. Jak pisaliśmy
we wstępie do tego rozdziału, może być ono uzupełnione o takie techniki jak unikanie błędów
i tolerowanie usterek. Znakomitego omówienia tego tematu dostarcza książka autorstwa
D. P. Siewiorka i R. S. Swarza [Siewiorek i Swarz, 1992].
U2TP to odpowiedź konsorcjum na propozycje OMG dotyczące opracowania profilu
UML dedykowanego testowaniu. Finalna wersja U2TP jest obecnie oficjalnym standardem
OMG [OMG, 2005]. Praktyczne wprowadzenie do testowania opartego na modelach, z wyko-
rzystywaniem U2TP, znajdą czytelnicy w książce P. Bakera, Z. R. Daiego i R. Grabowskiego
[Baker i in., 2008].

11.7. Ćwiczenia
11.1. Skoryguj usterki w metodach i sLeapYear() i getNumDaysInMonth() na listingu
11.2 i skonstruuj dla nich przypadki użycia z wykorzystaniem metody testowania
ścieżek. Czy Twoje przypadki użycia różnią się od przedstawionych w tabeli 11.6
i na rysunku 11.9? Dlaczego? Czy zaprojektowane przez Ciebie przypadki użycia
mogłyby wykryć usterki, które właśnie poprawiłeś?
11.2. Napisz w języku Java kod odpowiadający diagramowi stanów dla przypadku użycia
SetTime zegarka 2BWatch z rysunku 11.10. Skonstruuj przypadki testowe dla testo-
wania tego kodu metodami klas równoważności, testowania granicznego i testowania
ścieżek. Jak mają się te przypadki testowe do wykorzystywanych w testowaniu stero-
wanym stanami?
11.3. Skonstruuj diagram stanów odpowiadający przypadkowi użycia PurchaseTi cket
z tabeli 11.8 oraz przypadki testowe dla testowania sterowanego stanami. Prze-
dyskutuj problem liczby przypadków testowych i porównaj je z przypadkiem
PurchaseTi cket_CommonCase z tabeli 11.9.
11.4. Dla poniższej dekompozycji systemu
544 Rozdział 11. • Testowanie

skomentuj następujący plan testów:

Jakie decyzje menedżera odzwierciedla ten plan? Jakie są jego mocne i słabe strony?
11.5. Wyobraź sobie, że jesteś odpowiedzialny za testowanie integracyjne systemu szyfro-
wania danych przesyłanych przez sieć. Jednym z elementów tego systemów jest
podsystem generowania losowych kluczy. W czasie testowania integracyjnego pod-
system ten symulowany jest przez namiastkę dającą przewidywalne wyniki, jednakże
w docelowej wersji wspomniany podsystem faktycznie generować będzie klucze lo-
sowe, nieprzewidywalne dla obserwatora (intruza). Zaimplementuj infrastrukturę
testową dla tego systemu, wykorzystując jeden z wzorców projektowych opisywa-
nych w rozdziale 8. „Projektowanie obiektów: wielokrotne wykorzystywanie rozwią-
zań wzorcowych" w celu zapewnienia łatwej wymiany obu implementacji generatora.
Uzasadnij swój wybór.
11.6. Wykorzystaj testowanie ścieżek do skonstruowania przypadków testowych dla
wszystkich metod klasy NetworkConnection przedstawionej na rysunku 11.11 i li-
stingu 8.4. Rozpocznij od rozwinięcia kodu źródłowego w sposób eliminujący poli-
morfizm. Ile przypadków testowych musisz skonstruować do pokrycia wszystkich
ścieżek? Ile musiałbyś skonstruować dla kodu w oryginalnej (polimorficznej) postaci?
11.7. Znajdź analogię między terminologią z zakresu inżynierii oprogramowania z nastę-
pującymi określeniami użytymi w cytowanym na wstępie artykule R. Feynmana:
• pęknięcie łopatki,
• inicjacja pęknięcia,
• wysoka niezawodność silnika,
• zamierzenie projektantów,
• 10% początkowych zamiarów.
Co rozumie Feynman pod pojęciem „weryfikacji", używając sformułowania „wykryte
usterki są korygowane i zmodyfikowany komponent poddawany jest weryfikacji
w ramach następnych testów"?

Bibliografia
[Beck i Andres, 2005] K. Beck, C. Andres Extreme Programming Explained: Embrace Change,
wyd. drugie, Addison-Wesley, Reading, MA, 2005.
[Baker i in., 2008] P. Baker, Z. R. Dai, R. Grabowski Model-Driven Testing: Using the UML
Testing Profile, Springer, Berlin, 2008.
[Binder, 2000] R. V. Binder Testing Object-Oriented Systems: Models, Patterns, and Tools,
Addison-Wesley, Reading, MA, 2000.
Bibliografia 545

[Fagan, 1976] M. E. Fagan „Design and code inspections to reduce errors in program
development", IBM Systems Journal, t. 15, nr 3,1976.
[Feynman, 1988] R. P. Feynman „Personal observations on the reliability of the Shuttle",
Rogers Commission The Presidential Commission on the Space Shuttle
Challenger Accident Report, Washington, DC, czerwiec 1986.
[Grudin, 1990] J. Grudin „Obstacles to user involvement in interface design in large product
d e v e l o p m e n t organizations", Proceedings of IFIP INTERACT'90 Third
International Conference on Human-Computer Interaction, Cambridge,
U.K., sierpień 1990.
[Hopper, 1981] G. M. Hopper „The First Bug" Annals of the History of Computing 3,
str. 2 8 5 - 2 8 6 , 1 9 8 1 .
[IEEEStd. 829-2008] IEEE Standard for Software Test Documentation, IEEE Standards Board,
lipiec 2008.
[IEEEStd. 982.2-1988] IEEE Guide for the Use of IEEE Standard Dictionary of Measures to Produce
Reliable Software, IEEE Standards Board, czerwiec 1988.
[Jones, 1977] T. C. Jones „ P r o g r a m m e r quality a n d p r o g r a m m e r productivity",
IBM TR-02.764, 1977.
[JUnit, 2009] JUnit, http://www.junit.org/.
[Kelly, 1984] J. F. Kelly „An iterative design methodology for user-friendly natural
language office information applications", ACM Transactions on Information
Systems, t.2, nr 1, styczeń 1984.
[Mayhew, 1999] D. J. Mayhew The Usability Engineering Lifecycle: A Practitioner's Handbook
for User Interface Design, Morgan Kaufmann, 1999.
[McCabe, 1976] T. McCabe „A software complexity measure", IEEE Transactions on Software
Engineering, t.2, nr 12, grudzień 1976.
[Myers, 1979] G. J. Myers The Art of Software Testing, Wiley, New York, 1979. Wydanie
polskie: G. J. Myers, C. Sandler, T. Badgett, T. M. Thomas Sztuka testowania
oprogramowania, wyd. Helion 2005, http://helion.pl/ksiazki/artteo.htm.
[Nielsen i Mack, 1994] J. Nielsen, R L. Mack (red.) Usability Inspection Methods, Wiley, New York, 1994.
[OMG, 2005] Object M a n a g e m e n t G r o u p UML Testing Profile Version 1.0.
http://www.omg.org/ 2005.
[Parnas i Weiss, 1985] D. L. Parnas, D. M. Weiss „Active design reviews: principles and practice",
Proceedings of the Eighth International Conference on Software Engineering,
London, U.K., str. 132 - 136, sierpień 1985.
[Popper, 1992] K. Popper Objective Knowledge: An Evolutionary Approach, Clarendon,
Oxford, 1992.
[Porter i in., 1997] A. A. Porter, H. Siy, C.A. Toman, L.G. Votta „An experiment to assess the
costbenefits of code inspections in large scale software development" IEEE
Transactions on Software Engineering, t. 23, nr 6, str. 329 - 346, czerwiec 1997.
[Royce, 1998] W. Royce Software Project Management: A Unified Framework, Addison-
Wesley, Reading, MA, 1998.
[Rubin, 1994] J. Rubin Handbook of Usability Testing, Wiley, New York, 1994.
[Siewiorek i Swarz, 1992] D. P. Siewiorek, R. S. Swarz Reliable Computer Systems: Design and Evaluation,
wyd. drugie, Digital, Burlington, MA, 1992.
[Turner i Robson, 1993] C. D. Turner, D. J. Robson „The state-based testing of object-oriented
programs" Conference on Software Maintenance, str. 302 - 310, wrzesień 1993.
12.1. Wstęp: przycinanie wędzonej szynki 550

12.2. O racjonalizacji ogólnie 551

12.3. Koncepcje racjonalizacji 554


12.3.1. CTC — system centralnego sterowania ruchem 555
12.3.2. Definiowanie problemów: zagadnienia 556
12.3.3. Eksploracja przestrzeni rozwiązań: propozycje 557
12.3.4. Wartościowanie elementów przestrzeni rozwiązań:
kryteria i argumenty 559
12.3.5. Kolapsacja przestrzeni rozwiązań: rozstrzygnięcie 560
12.3.6. Implementowanie rozstrzygnięć: elementy działania 561
12.3.7. Przykłady modeli zagadnień i ich realizacje 562
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 567
12.4.1. Projekt systemu CTC 568
12.4.2. Kolekcjonowanie racjonalizacji w ramach zebrań 569
12.4.3. Asynchroniczne kolekcjonowanie racjonalizacji 577
12.4.4. Racjonalizacja dyskutowanych zmian 579
12.4.5. Rekonstruowanie racjonalizacji 582

12.5. Kierownicze aspekty zarządzania racjonalizacją 585


12.5.1. Dokumentowanie racjonalizacji 585
12.5.2. Przypisywanie odpowiedzialności 587
12.5.3. Heurystyki komunikowania racjonalizacji 588
12.5.4. Modelowanie i negocjowanie zagadnień 589
12.5.5. Strategie rozwiązywania konfliktów 590

12.6. Literatura uzupełniająca 592

12.7. Ćwiczenia 593

Bibliografia 594
Zarządzanie racjonalizacją
Można opisywać motocykl w kategoriach „co?" i „jak", czyli w kategoriach
komponentów, z których się składa, oraz zasad, według których
komponenty te funkcjonują; oglądając ilustrację motocykla, można
się jednak tylko domyślać tych wszystkich „gdzie?" i „dlaczego?"
decydujących o takim, a nie innym kształcie poszczególnych części
i ich wzajemnym rozmieszczeniu.
— Robert Pirsig Zen i sztuka oporządzania motocykla

w kontekście realizowania projektów pojęcie „racjonalizacja"1 (rationale) oznacza uzasad-


nianie podejmowanych decyzji. Dotychczas w treści tej książki opisywaliśmy modele repre-
zentujące różne oblicza systemu; model racjonalizacji reprezentuje natomiast powody, dla
których funkcjonalność systemu i jej implementacja przyjmują taki, a nie inny kształt. Racjo-
nalizacja ma kluczowe znaczenie w co najmniej dwóch aspektach realizacji projektu: wspoma-
ganiu podejmowania decyzji oraz kolekcjonowaniu wiedzy. Na tak pojmowaną racjonalizację
składają się:

• problemy wymagające rozwiązywania,


• możliwe ewentualności rozwiązań tych problemów,
• decyzje podejmowane w związku z rozwiązywaniem problemów,
• kryteria uwzględniane przy podejmowaniu decyzji,
• dyskusje programistów prowadzone w związku z wypracowywaniem decyzji.

Racjonalizacja przyczynia się do jakości podej mo wanych decyzji, bo ujawniania podsta-


wowe elementy decyzyjne — kryteria, priorytety i argumenty. Jeśli chodzi o kolekcjonowanie
wiedzy, racjonalizacja ma znaczenie krytyczne w sytuacji, gdy pojawia się konieczność zmian:
przy wprowadzaniu do systemu nowej funkcjonalności programiści mają możliwość prześle-
dzenia podjętych dotychczas decyzji w kontekście ich uzasadnienia, sensowności, adekwatno-
ści i tym podobnych; gdy do zespołu dołączają nowi programiści, mogą łatwo zapoznać się ze
wspomnianymi decyzjami, studiując dokumenty racjonalizacji systemu.
Niestety, racjonalizacja należy do najbardziej złożonych kategorii informacji zarówno
w kontekście jej tworzenia, jak i utrzymywania w aktualnym stanie. Zarządzanie racjonalizacją
jest bowiem inwestycją, która przynosi zysk dopiero w dłuższej perspektywie.

1
Ponieważ większość badań związanych z racjonalizacją koncentrowała się początkowo na etapie
projektowania systemu, do samego rzeczownika „racjonalizacja" przylgnął na dobre przymiotnik
„projektowa" (design rationale). Jak jednak pokazujemy w tym rozdziale, racjonalizacja jest nie-
rozłącznie związana ze wszystkimi etapami projektu, dlatego będziemy o niej pisać bez przymiot-
ników wiążących ją z konkretnymi etapami czy aktywnościami.
550 Rozdział 12. • Zarządzanie racjonalizacją

W tym rozdziale opiszemy modelowanie zagadnień jako reprezentację modelowania ra-


cjonalizacji, omówimy także aktywności związane z tworzeniem, pielęgnowaniem i wykorzy-
stywaniem modeli racjonalizacji. Rozdział zakończymy opisem zagadnień menedżerskich
związanych z racjonalizacją, takich jak wspomaganie podejmowania decyzji i negocjowanie.

12.1. Wstęp: przycinanie wędzonej szynki


Modele systemu są abstrakcjami wykonywanych przez niego funkcji. Modele związane z ana-
lizą wymagań — model przypadków użycia, model klas i model sekwencji (o których pisaliśmy
w rozdziałach 4. „Zbieranie wymagań" i 5. „Analiza wymagań") — reprezentują zachowanie
systemu z perspektywy jego użytkownika. Model projektu systemu (patrz rozdział 6. „Projek-
towanie systemu — dekompozycja na podsystemy") stanowi reprezentację systemu w kon-
tekście podziału na podsystemy, celów projektowych, węzłów sprzętowych, przechowywania
danych, kontroli dostępu i tak dalej. Model racjonalizacji systemu reprezentuje natomiast
całokształt decyzji, przesłanek, powodów i tym podobnych, decydujących o tym, dlaczego
system ten zbudowany jest i działa właśnie tak, a nie inaczej.
Rozważmy najpierw, dlaczego w ogóle powinniśmy się zastanawiać nad wspomnianym
„dlaczego?" — na początek mała dygresja2.

Mary zapytała kiedyś swego męża Johna, dlaczego przycina szynkę na obu końcach przed włoże-
niem jej do wędzarni. John odparł, że tak robiła jego mama, prawdopodobnie zgodnie z jakimś prze-
pisem, on sam nigdy się nad tym nie zastanawiał.

Zaciekawiona Mary zapytała więc o to swą teściową, Ann. Ta odrzekła, że jej mama, Zoe, wymyśliła
taki właśnie sposób na poprawienie smaku wędzonej szynki

Indagowana na tę okoliczność Zoe wyraziła głębokie zdziwienie: nigdy nie przycinała szynki przed
wędzeniem, a już sugestia, jakoby takie przycinanie miało poprawiać smak szynki, jest kompletnym
absurdem dla kogoś, kto ma choć blade pojęcie na temat kulinariów. Po chwili Zoe przypomniała
sobie jednak, że gdy Ann była małą dziewczynką, zdarzyło się wielokrotnie, iż standardowych roz-
miarów szynka nie mieściła się w zbyt ciasnym piecu i faktycznie trzeba ją było skracać o kilka cen-
tymetrów z obu końców. Wkrótce jednak kupiono nową, większą wędzarnię i technologia skracania
szynki poszła w zapomnienie.

Programiści i kucharze uwielbiają rozpowszechniać rozmaite pomysły, sposoby, techniki


i tym podobne, którym jednak nie towarzyszy wystarczająca racjonalizacja, która mogłaby
zwiększyć ich przydatność w konkretnym zastosowaniu. Wszyscy znamy tak zwany problem
roku 2000: w latach 60. i 70. ubiegłego wieku wysokie koszty pamięci i magazynowania da-
nych skłaniały do poszukiwań różnych optymalizacji rozmiaru tych danych i jedną z takich
technik było zapisywanie tylko dwóch końcowych cyfr roku (na przykład „78"), dwie pierwsze
(„19") były bowiem niezmienne. Co do tworzonego i używanego wówczas oprogramowania
przyjmowano (jako pewnik) założenie, że w roku 2000 pozostanie już po nim co najwyżej
wspomnienie. Czas mijał nieubłaganie, pamięci RAM i dyski drastycznie staniały, a progra-
miści w dalszym ciągu nie przejmowali się problemem zapisywania cyfr reprezentujących stu-
lecie. Ludzkość wkroczyła dumnie w XXI wiek i zaczęły się problemy z operacjami arytme-

2
Popularna historyjka niewiadomego autorstwa.
12.2. O racjonalizacji ogólnie 551

tycznymi wykonywanymi na dwucyfrowych zapisach lat: piszący te słowa tłumacz, urodzony


w roku 1958, latem 2001 roku ukończył (jak to wyliczył jeden z programów) 01-58 = - 57 lat,
a jego dziadek, świętujący w 2004 roku 105. urodziny, zaproszony został do lokalnego
centrum kultury na (darmową) imprezę dla ... pięciolatków. I nie można przy tym zrzucać
całej winy na pokolenie programistów lat 90.: zdając sobie sprawę ze zbliżającego się fin de
siecle, skrępowani byli jednocześnie względami kompatybilności wstecz z eksploatowanym
oprogramowaniem — w pewnym sensie kompatybilności z krótkowzrocznością swych star-
szych kolegów. W rezultacie, mimo iż mamy już za sobą pierwszą dekadę nowego stulecia,
wiele używanych dziś programów dotkniętych jest „syndromem Y2K", jak zwykło się nazywać
(z angielska) opisany problem.
Spadek cen pamięci czy kupno większej wędzarni to przykład zmian, które właściwie
rozumieć i wykorzystywać (lepiej sobie z nimi radzić) pozwala właśnie modelowanie racjona-
lizacji. Kolekcjonowanie uzasadnień podejmowanych decyzji umożliwia modelowanie zależ-
ności między tymi decyzjami i początkowo przyjmowanymi założeniami. Gdy zmieniają się
założenia, należy ponownie rozważyć zasadność decyzji podjętych na ich podstawie. W tym
rozdziale opiszemy techniki kolekcjonowania, pielęgnowania i wykorzystywania modeli ra-
cjonalizacji, w szczególności:

• ogólny pogląd na modelowanie racjonalizacji (patrz sekcja 12.2),


• koncepcje związane z modelami racjonalizacji (patrz sekcja 12.3),
• aktywności związane z tworzeniem i wykorzystywaniem modeli racjonalizacji (patrz
sekcja 12.4),
• zadania menedżera projektu związane z utrzymywaniem modeli racjonalizacji (patrz
sekcja 12.5).

Zacznijmy jednak od koncepcji samego modelu racjonalizacji.

12.2. O racjonalizacji ogólnie


Racjonalizacja decyzji to zespół motywacji kryjących się za jej podjęciem. Na racjonalizację
tę składają się:

• Zagadnienie (issue). Każda decyzja związana jest z rozwiązywaniem konkretnego


problemu. Klarowny opis tego problemu jest kluczowym elementem racjonalizacji;
w związku z przedstawionymi przykładami pytania te brzmią „Jak należy wędzić
szynkę?" oraz „W jakiej postaci prezentować rok kalendarzowy?".
• Ewentualności (alternatives). Ewentualnościami nazywamy możliwe sposoby roz-
wiązania danego problemu, w tym również sposoby, które nie zostały zastosowane
ze względu na niespełnienie jednego lub więcej określonych kryteriów. Przykładowo
duży piec wędzarniczy może być zbyt drogi, a przechowywanie liczb oznaczających
lata w postaci dwubajtowych słów zamiast bajtów może spowolnić szybkość obliczeń
i zwiększyć ilość wymaganej pamięci.
552 Rozdział 12. • Zarządzanie racjonalizacją

• Kryteria (criteria). Określają one cechy, którymi charakteryzować się powinno roz-
wiązanie problemu — i tak na przykład przepis na uwędzenie szynki powinien być
wykonalny przy użyciu podstawowego wyposażenia domowej kuchni, a programiści
decydujący się pół wieku temu na dwucyfrowy zapis roku dążyli do oszczędności
pamięci masowych. Na etapie analizy wymagań wspomniane kryteria mają postać
wymagań pozafunkcyjnych i ograniczeń dotyczących między innymi łatwości użyt-
kowania systemu czy oczekiwanej liczby błędów popełnianych codziennie przez
użytkownika wprowadzającego dane wejściowe. Na etapie projektowania systemu
kryteria te wyrażają cele projektowe w postaci niezawodności, czasu reakcji i tym
podobnych. Z perspektywy menedżera projektu kryteria te wyrażają jego specy-
ficzne cele oraz kompromisy, które musi rozstrzygać — na przykład kompromis
między jakością systemu a terminem jego dostarczenia.
• Argumentacja (arguments). Zarówno sztuka kulinarna, jak i inżynieria oprogra-
mowania nie są dyscyplinami algorytmicznymi, bowiem kucharze i programiści
napotykają rozmaite problemy i poszukują różnych sposobów ich rozwiązywania,
uwzględniając zalety i wady danego sposobu w porównaniu z innymi. Argumento-
wanie dotyczy wszelkich aspektów procesu decyzyjnego — kryteriów, uzasadnień,
rozważanych ewentualności oraz rozstrzyganych kompromisów.
• Decyzje (decisions). Decyzja to rozwiązanie problemu, reprezentujące konkretną
ewentualność wybraną zgodnie z określonymi kryteriami ewaluacji. Kilkucentyme-
trowe skracanie wędzonej szynki czy ignorowanie cyfr stulecia w zapisie lat to przy-
kłady konkretnych decyzji. Konkretny kształt modelu analitycznego czy modelu
projektu systemu jest niczym innym, jak skumulowanym efektem ciągu podjętych
decyzji. Być może wiele z tych decyzji podjętych zostało bez starannego przeanalizo-
wania rozwiązywanego problemu lub należytej eksploracji możliwych ewentualności.

Przez cały czas realizowania projektu, na wszystkich jego etapach, zmuszeni jesteśmy
do podejmowania licznych decyzji, wspomaganych modelami racjonalizacji, i tak:

• W czasie zbierania wymagań i ich analizy decydujemy o kształcie funkcjonalnym


systemu, przy wydatnej współpracy z klientem. Podejmowane decyzje motywowane
są przez użytkownika lub przez potrzeby organizacyjne. Uzasadnienie tych decyzji
staje się użyteczne przy tworzeniu przypadków testowych na potrzeby testów inte-
gracyjnych i testów akceptacyjnych.
• W czasie projektowania systemu formułujemy cele projektowe i determinujemy kształt
dekompozycji systemu na podsystemy. Decyzje związane z celami projektowymi
wynikają najczęściej z wymagań pozafunkcyj nych, tak więc kolekcjonowanie ra-
cjonalizacji tych decyzji umożliwia śledzenie zależności między dwiema encjami
— wymaganiami pozafunkcyjnymi i celami projektowymi: gdy zmienią się wyma-
gania, łatwiejsze będzie odpowiednie przeformułowanie celów projektowych.
• Zarządzanie projektem wiąże się z przyjmowaniem wielu różnych założeń związanych
z relatywnym ryzykiem projektowym — i tak na przykład komponenty utworzone
stosunkowo niedawno wymagają (statystycznie) więcej fatygi ze strony programistów
12.2. O racjonalizacji ogólnie 553

niż komponenty w miarę dojrzałe. Kolekcjonowanie racjonalizacji ryzykownych


decyzji i opracowywanie „planów odwrotu" umożliwi złagodzenie ewentualnych pro-
blemów, które wynikać mogą z tego ryzyka.
• W czasie testowania i integrowania podsystemów wykrywamy niezgodności między
ich interfejsami. Na podstawie racjonalizacji kryjącej się za konkretnym kształtem
tych interfejsów możemy łatwo zidentyfikować założenie, którego niespełnienie
okazało się przyczyną usterki i usunąć tę usterkę przy minimalnym wpływie na resztę
systemu.

Zarządzanie racjonalizacją jest inwestycją, wymagającą określonych zasobów dedyko-


wanych zarządzaniu zmianami: kolekcjonując pewne informacje teraz, ułatwiamy sobie póź-
niejsze weryfikowanie podejmowanych obecnie decyzji, w związku z wprowadzanymi zmia-
nami w systemie. Wielkość wspomnianych zasobów zależna jest od konkretnego typu systemu.
Gdy na przykład tworzymy skomplikowany system dla konkretnego klienta, system ten
będzie z pewnością doznawał wielu zmian w dłuższej perspektywie; klient, który jest świadom
tego faktu, może wówczas nawet zażądać kolekcjonowania racjonalizacji także na swój użytek.
Jeśli natomiast tworzymy koncepcyjny prototyp nowego produktu, prototyp ten prawdopo-
dobnie okaże się nieprzydatny, gdy rozpoczną się prace nad rzeczywistym produktem. Kolek-
cjonowanie racjonalizacji na etapie tworzenia wspomnianego prototypu może opóźnić jego
demonstrację, co stwarzać może ryzyko „skasowania" całego projektu; zarządzanie racjonali-
zacją jest w tym wypadku inwestycją nieopłacalną, bo w najlepszym razie dającą korzyści nie-
współmierne do ponoszonego ryzyka.
Kolekcjonowanie racjonalizacji może mieć różną intensywność, odzwierciedlaną w po-
staci czterech następujących poziomów:

• brak wyraźnego kolekcjonowania — informacja związana z racjonalizacją ma jedynie


formę ulotną, bo istnieje tylko w umysłach programistów, a w najlepszym razie
— w listach, faksach i innej postaci komunikatach wymienianych między nimi.
Całość jawnej dokumentacji systemu skupia się w jego modelach.
• rekonstruowanie racjonalizacji — ulotnej (w sensie powyżej opisanym) informa-
cji związanej z racjonalizacją nadaje się fizyczną postać w ramach aktywności do-
kumentacyjnych projektu. Kryteria projektowe i motywacje kryjące się za najważ-
niejszymi decyzjami architektonicznymi integrowane są z odpowiednimi modelami
systemu. Odrzucone ewentualności oraz argumenty „za" i „przeciw" nie zostają jawnie
udokumentowane.
• bieżące kolekcjonowanie racjonalizacji, w związku z każdą podejmowaną decyzją.
Informacja związana z racjonalizacją reprezentowana jest w formie odrębnego mo-
delu, zawierającego odniesienia do innych modeli — i tak na przykład modele za-
gadnień reprezentują racjonalizację w formie grafu, którego każdy węzeł prezentuje
zagadnienie, ewentualność lub kryteria ewaluacji. Racjonalizacja modelu analitycz-
nego może być wówczas reprezentowana przez powiązania poszczególnych zagad-
nieńz odpowiednimi przypadkami użycia.
• zintegrowanie racjonalizacji z modelami systemu sprawia, że staje się ona central-
nym modelem systemu. Pojawiające się sukcesywnie nowe elementy racjonalizacji
zapisywane są w „żywej", przeszukiwalnej, informacyjnej bazie danych. Wszelkie
554 Rozdział 12. • Zarządzanie racjonalizacją

zmiany w systemie rozpoczynają swój żywot od zarejestrowania ich w tej bazie, w for-
mie dyskusji towarzyszącej poszczególnym decyzjom. Wspomniana baza stanowi
zatem swoiste podsumowanie decyzji, których konsekwencje rozproszone są po
modelach systemu.

W dwóch pierwszych przypadkach miejscem przechowywania informacji związanej


z racjonalizacją są umysły programistów; dwa następne przypadki reprezentują fizyczne umo-
cowanie wspomnianej informacji, która tym samym staje się niezależna od umysłu ludzkiego,
zawodnego i ograniczonego pod względem ilościowym. Wybór złotego środka między dwiema
skrajnościami — brakiem fizycznego reprezentowania racjonalizacji a jej ścisłą integracją
z modelami systemu — uzależniony jest od możliwości zainwestowania zasobów w owo repre-
zentowanie, we wczesnych etapach realizacji projektu. W tym rozdziale skupimy się wyłącznie
na dwóch ostatnich z czterech wymienionych poziomów.
Oprócz korzyści długofalowych, jakie daje kolekcjonowanie racjonalizacji, przynosić
ono może także korzyści doraźne. Po pierwsze, zmusza do podejmowania decyzji przemyśla-
nych, niedyktowanych emocjami — a przynajmniej pozwala odróżnić decyzje starannie prze-
myślane od podejmowanych w obliczu presji czy innych „pilnych" okoliczności. Po drugie,
pozwala programistom lepiej rozumieć decyzje podejmowane przez kolegów.
Modele racjonalizacji, w porównaniu z innymi modelami systemu, zawierają wyraźnie
więcej informacji, zmieniającej się w wyraźnie szybszym tempie. Złożoność tej informacji
stwarza potrzebę opracowania technik zarządzania nią w sposób sformalizowany. Za chwilę
opiszemy reprezentowanie racjonalizacji w formie modeli zagadnień.

12.3. Koncepcje racjonalizacji


W tej sekcji opiszemy modelowanie zagadnień, opierające się na założeniu, że aktywności
programistów są działaniami dialektycznymi, zmierzającymi do rozwiązywania problemów
drogą poszukiwania i ewaluowania różnych ewentualności. Modelując argumenty przema-
wiające „za" i „przeciw" wspomnianym ewentualnościom, tworzymy reprezentację racjonali-
zacji obejmującą:

• zagadnienia reprezentujące problemy projektowe i pytania związane z projektem


(patrz sekcja 12.3.2),
• ewentualności rozwiązań tych problemów (patrz sekcja 12.3.3),
• argumenty „za" poszczególnymi rozwiązaniami i „przeciw" nim (patrz sekcja 12.3.4),
• decyzje wyboru konkretnych rozwiązań (patrz sekcja 12.3.5),
• implementacje podjętych decyzji w postaci przydziału zadań (patrz sekcja 12.3.6).

W sekcji 12.3.7 dokonamy przeglądu wybranych systemów reprezentowania proble-


mów — systemów o znaczeniu historycznym. Najpierw jednak przyjrzyjmy się szczegółowo
scentralizowanemu systemowi kierowania ruchem kolei miejskiej, który to system stanowić
będzie kanwę do budowania rozmaitych przykładów na potrzeby tego rozdziału.
12.3. Koncepcje racjonalizacji 555

12.3.1. CTC — system centralnego sterowania ruchem


Scentralizowany system sterowania ruchem (w dalszym ciągu określać go będziemy skrótem
CTC, od Centralized Traffic Control) umożliwia zdalne monitorowanie ruchu pociągów i zdalne
sterowanie tym ruchem. Każda trasa podzielona jest na tak zwane odcinki izolowane, z któ-
rych każdy reprezentuje najmniejszy niepodzielny odcinek monitorowania. Zadaniem sygna-
lizatorów i innych urządzeniem jest stworzenie gwarancji, że w danej chwili na każdym ze
wspomnianych odcinków znajduje się co najwyżej jeden pociąg. Gdy pociąg wjeżdża na dany
odcinek izolowany, specjalne czujniki rejestrują ten fakt i powodują wyświetlenie na monitorze
dyspozytora (w odpowiedniej lokalizacji, odpowiadającej danemu odcinkowi) informacji za-
wierającej między innymi identyfikator pociągu. Do łączenia odcinków w kompletne trasy służą
zwrotnice, zarządzane zdalnie przez dyspozytorów. Zbiór wszystkich odcinków znajdujących
się pod kontrolą określonego dyspozytora nazywamy „sekcją dyspozycyjną".
Na rysunku 12.1 przedstawiona jest uproszczona postać interfejsu użytkownika systemu
CTC. Odcinki izolowane reprezentowane są przez linie, zwrotnice — przez zbieg trzech lub
czterech linii. Sygnały reprezentowane są przez ikony odzwierciedlające dwa stany: „stój" i „droga
wolna". Zwrotnice, pociągi i sygnalizatory opatrzone są unikalnymi identyfikatorami, używa-
nymi przez dyspozytora w celu wydawania poleceń. Na rysunku 12.1 widzimy zatem sygnali-
zatory SI, S2, S3 i S4, zwrotnice SWl i SW2 i pociągi T1291 i T1515. Komputery zlokalizowane
w pobliżu odcinków, zwane „stacjami przydrożnymi", stanowią zabezpieczenie gwarantujące,
że grupa sygnalizatorów i zwrotnic nigdy nie znajdzie się w stanie zagrażającym bezpieczeń-
stwu — czyli na przykład sygnał „droga wolna" nie zostanie wyświetlony równocześnie na
sygnalizatorach SI i S2. Stacje przydrożne zostały tak zaprojektowane, że zapewniają bezpie-
czeństwo nawet w przypadku awarii całego systemu, bowiem system ten komunikuje się z sy-
gnalizatorami i zwrotnicami wyłącznie za ich pośrednictwem. Sam system nie musi więc być
„całkowicie bezpieczny" (failsafe), choć jego ewentualne awarie powinny mieć charakter
sporadyczny.

Rysunek 12.1. Uproszczony przykład wyświetlanej sekcji dyspozycyjnej CTC

W latach 60. ubiegłego wieku pulpity sterownicze systemu CTC miały postać dedyko-
wanych urządzeń wyposażonych w żarówki wyświetlające stan poszczególnych odcinków
izolowanych oraz przyciski służące do przestawiania zwrotnic i ustawiania sygnalizacji. W la-
tach 70. dedykowane pulpity zastąpione zostały monitorami kineskopowymi, umożliwiają-
cymi wyświetlanie większej ilości szczegółów na mniejszej powierzchni. Ostatnio monitory
556 Rozdział 12. • Zarządzanie racjonalizacją

te ustąpiły miejsca stacjom roboczym umożliwiającym tworzenie bardziej wyrafinowanych


interfejsów dla dyspozytorów i pozwalającym na rozproszenie przetwarzania między kilka
komputerów. Mimo iż system nie jest krytyczny dla życia i zdrowia ludzkiego — nad którego
bezpieczeństwem czuwają stacje przydrożne — jednak jego awaria spowodować może chaos
komunikacyjny, przekładający się (między innymi) na straty ekonomiczne. W konsekwencji
wszelkie transformacje technologiczne CTC — takie jak zastępowanie dedykowanych pulpitów
monitorami czy wymiana tych monitorów (połączonych z komputerami mainframe) na stacje
robocze — powinny odbywać się bardziej stopniowo niż w innych systemach. Tak oto CTC
jawi się jako dziedzina, w której kolekcjonowanie racjonalizacji jest czynnikiem krytycznym —
i dlatego właśnie wybraliśmy ją jako przykładową na potrzeby tego rozdziału.

12.3.2. Definiowanie problemów: zagadnienia


Zagadnienie to reprezentacja konkretnego problemu, którym może być wymaganie, pro-
jekt lub element zarządzania. Jak szybko dyspozytor powinien być informowany o opóźnieniu
pociągu? W jaki sposób powinny być przechowywane trwałe dane? Która technologia niesie ze
sobą największe ryzyko? Zagadnienia często reprezentują problemy, dla których nie istnieje
jedyne poprawne rozwiązanie i które z tego powodu nie mogą być rozwiązywane algorytmicz-
nie. Zagadnienia rozwiązywane są zwykle w drodze dyskusji i negocjacji.
W języku UML zagadnienia reprezentowane są przez instancje klasy Issue. W klasie tej
definiowany jest atrybut subject reprezentujący streszczenie zagadnienia, atrybut description
reprezentujący bardziej szczegółowy opis zagadnienia i jego związki z innymi materiałami
oraz atrybut status informujący, czy zagadnieniezostało rozwiązane („zamknięte" — cl osed),
czy nie („otwarte" — open). Zagadnienia zamknięte mogą być ponownie otwierane. Zgodnie
z konwencją zagadnienia opatrywane są krótkimi nazwami, na przykład train del ay?, umoż-
liwiającymi ich jednoznaczną identyfikację. Na rysunku 12.2 przedstawiliśmy reprezentację
trzech zagadnień sformułowanych w poprzednim akapicie.

train del ay?: Issue Jak szybko dyspozytor powinien być informowany o opóźnieniu pociągu?

storage?: Issue W jaki sposób powinny być przechowywane trwałe dane?

technology risk?:Issue Która technologia niesie ze sobą największe ryzyko?

Rysunek 12.2. Przykłady zagadnień

Zagadnienia pojawiające się w związku z realizacją projektu zwykle bywają powiązane,


między innymi zagadnienia mogą być dekomponowane na prostsze podzagadnienia. Zagad-
nienie Jaki powinien być czas reakcji w systemie sterowania ruchem? automatycznie generuje
zagadnienie Jak szybko dyspozytor powinien być informowany o opóźnieniu pociągu? Zresztą,
samo tworzenie systemu CTC może być postrzegane jako pojedyncze złożone zagadnienie/a/cz
12.3. Koncepcje racjonalizacji 557

system kontroli ruchu powinniśmy zbudować?, podlegające dekomponowaniu na znaczną liczbę


prostszych podzagadnień. Zagadnienia mogą być także generowane przez decyzje podejmowane
w związku z innymi zagadnieniami. Przykładowo zagadnienie dotyczące cache owania danych
w węźle lokalnym generuje natychmiast zagadnienia związane z utrzymywaniem spójności
między centralnym egzemplarzem danych a jego replikami w węzłach lokalnych. Takie wtórne
zagadnienia nazywamy zagadnieniami wynikowymi (consequent issues).
Powróćmy do systemu CTC i wyobraźmy sobie, że rozważamy jego migrację z kom-
putera mainframe na sieć stacji roboczych. W nowej wersji systemu każdy dyspozytor będzie
korzystał z osobnej stacji roboczej komunikującej się z serwerem, który zapewnia komunikację
ze stacjami przydrożnymi. W czasie dyskusji na ten temat wyniknęły dwa zagadnienia: W jaki
sposób dyspozytor powinien wydawać polecenia systemowi? oraz W jakiej postaci na ekranie
stacji roboczej ma być wyświetlany stan odcinków izolowanych?. Na rysunku 12.3 zagad-
nienia te przedstawione są na diagramie obiektów UML.

input?: Issue W jaki sposób dyspozytor powinien wydawać polecenia systemowi?

display?: Issue W jakiej postaci na ekranie stacji roboczej ma być wyświetlany stan
odcinków izolowanych?

Rysunek 12.3. Zagadnienia związane z interfejsem CTC

Zagadnienie powinno koncentrować się wyłącznie na problemie, nie na możliwych


ewentualnościach jego rozwiązania (ewentualności te są bowiem przedmiotem propozycji,
o których pisać będziemy w następnej sekcji). Konwencją promującą tę regułę jest formuło-
wanie zagadnień w formie pytań; aby dodatkowo wyeksponować tę regułę, dołączać będziemy
znak zapytania na końcu nazwy zagadnienia.

12.3.3. Eksploracja przestrzeni rozwiązań: propozycje


Propozycja jest jedną z odpowiedzi na zagadnienie. Dyspozytor nie musi być informowany
o opóźnieniach pociągów to propozycja związana z zagadnieniem Jak szybko powinien być
dyspozytor informowany o opóźnieniu pociągu? Propozycja niekoniecznie musi być dobra
czy poprawna w kontekście zagadnienia, z którym jest powiązana. Propozycje umożliwiają
dogłębne eksplorowanie przestrzeni rozwiązań: często w czasie burzy mózgów nawet bezsen-
sowne propozycje stają się źródłem nowych pomysłów, które w innych okolicznościach być
może nie miałyby szansy zaistnienia. Różne propozycje związane z tym samym zagadnieniem
mogą się zazębiać, tak jak na przykład dla zagadnienia Jak przechowywać trwałe dane? propo-
zycje Należy użyć relacyjnej bazy danych oraz Należy użyć relacyjnej bazy danych dla danych
strukturalnych i „płaskich"plików dla obrazów i dźwięków. Propozycje służą do reprezentowania
zarówno wybranych rozwiązań dla zagadnień, jak i odrzuconych ewentualności rozwiązania.
Jedna propozycja może odnosić się do kilku zagadnień, na przykład propozycja Należy
wykorzystać architekturę „model-widok-kontroler" może być adresowana do zagadnień Jak
odseparować obiekty interfejsu od obiektów encji? oraz Jak zapewnić spójność między wieloma
558 Rozdział 12. • Zarządzanie racjonalizacją

widokami tego samego obiektu?. Propozycja może stać się także źródłem nowego zagadnienia:
propozycja Należy wykorzystywać automatyczne odśmiecanie jako odpowiedź na zagadnienie
Jak minimalizować wycieki pamięci? prowadzi natychmiast do zagadnienia Jak minimalizować
degradację reaktywności systemu spowodowaną automatycznym zarządzaniem pamięcią?.
Rozważając sensowność propozycji w kontekście danego zagadnienia, należy brać pod uwagę
także wszystkie jego zagadnienia wynikowe.
Na diagramach UML propozycje reprezentowane są jako instancje klasy Proposal.
W klasie tej (podobnie jak w klasie Issue) definiowane są atrybuty subject i descri pti on. Zgod-
nie z konwencją, nazwa propozycji powinna mieć formę krótkiej frazy czasownikowej. Propo-
zycje jako obiekty klasy Proposal powiązane są z odnośnymi zagadnieniami, jako obiektami
klasy Issue, za pomocą dwojakiego typu skojarzeń: skojarzenie addressed by wskazuje za-
gadnienie, dla którego dana propozycja jest odpowiedzią, zaś skojarzenie raises wskazuje
jedno lub kilka zagadnień generowanych przez daną propozycję.
Powróćmy do CTC. Rozważając zagadnienie związane z interfejsem dyspozytora, ma-
my do wyboru dwie propozycje: graficzny interfejs typu „wskaż i kliknij" (poi nt&cl ick)
oraz interfejs tekstowy (text-based), w ramach którego odcinki izolowane reprezentowane
są w postaci znaków semigraficznych. Propozycja interfejsu tekstowego generuje zagadnienie
wynikowe dotyczące wyboru konkretnej emulacji terminala. Wzajemne powiązanie tych za-
gadnień i propozycji widoczne jest na rysunku 12.4.

poi nt&cl i c k : Proposal Interfejs dyspozytora powinien być zrealizowany jako interfejs GUI
typu „wskaż i kliknij".

text only: Proposal Interfejs dyspozytora powinien mieć postać tekstową, a odcinki izolowane
powinny być wyświetlane za pomocą znaków semigraficznych.

terminal?: Issue Jakiej emulacji terminala należy użyć?

Rysunek 12.4. Przykład propozycji i zagadnienia wynikowego. Propozycje i zagadnienie wynikowe


generowane przez jedną z nich wyróżnione zostały pogrubioną ramką

Propozycja powinna zawierać jedynie meritum proponowanego rozwiązania, w oderwa-


niu od jego oceny, zalet i wad, te bowiem są domeną innych elementów UML — kryteriów
i argumentów.
12.3. Koncepcje racjonalizacji 559

12.3.4. Wartościowanie elementów przestrzeni rozwiązań:


kryteria i argumenty
Kryterium wyraża pożądaną jakość propozycji przynależnej do określonego zagadnienia.
Cele projektowe, takie jak na przykład czas reakcji systemu czyjego niezawodność, to kryteria
związane z celami projektowymi. Cele menedżerskie, w postaci minimum kosztów i minimum
ryzyka, to kryteria związane z zagadnieniami wynikającymi z zarządzania projektem. Zbiór
kryteriów wskazuje kierunki oceniania poszczególnych propozycji. Propozycja spełniająca
określone kryteriów kwalifikowana jest jako oceniona pozytywnie, propozycja niespełniająca
go — jako oceniona negatywnie, w kontekście tegoż kryterium. Dane kryterium może być
współdzielone przez wiele zagadnień.
Na diagramach UML kryteria reprezentowane są w postaci instancji klasy Criterion.
Klasa ta, podobnie jak klasa Issue, posiada atrybuty subject i description. Atrybut subject
zawsze wyrażany jest w ukierunkowaniu pozytywnym, to znaczy musi wyrażać kryterium, które
poszczególne propozycje powinny maksymalizować — czyli na przykład szybkość, reaktywność,
niski koszt, a nie czas czy koszt (pożądanymi cechami są bowiem maksymalna szybkość,
maksymalna reaktywność i maksymalnie niski koszt, ale nie maksymalny czas i maksymalny
koszt). Kryteria (jako obiekty klasy Cri terion) wiązane są z odpowiednimi propozycjami
(obiektami klasy Proposal) za pomocą skojarzeń assessment. Skojarzenie posiada atrybut
value, wyrażający propozycję w kontekście danego kryterium („spełniająca" — meets — albo
„niespełniająca" — fai 1 s), oraz atrybut we i ght, wyrażający znaczenie („wagę") propozycji
w kontekście tegoż kryterium. Zgodnie z konwencją, na końcu nazwy kryterium dołącza się
znak dolara ($) dla zaznaczenia, że kryteria stanowią miarę dobroci i nie powinny być mylone
z zagadnieniami czy argumentami.
W związku z interfejsem dyspozytora CTC formułujemy dwa kryteria: dostępności
(avai 1 ab i 1 i ty) jako niefunkcyjne wymagania jak najdłuższej pracy bez awarii i użyteczności
(usabl i 1 i ty) rozumianej w tym przypadku jako minimalizacja czasu wydawania popraw-
nych poleceń (patrz rysunek 12.5). Kryteria te wyprowadzone zostały wprost z niefunkcyj-
nych wymagań dla systemu. W kontekście tych kryteriów rozpatrujemy obie propozycje do-
tyczące wariantów technologii interfejsu (GUI albo tekstowy), i tak interfejs GUI okazuje się
niezgodny z kryterium dostępności, jest bowiem bardziej skomplikowany, a więc trudniejszy
do przetestowania i bardziej podatny na błędy; interfejs GUI okazuje się jednak lepszy z per-
spektywy kryterium użyteczności, ze względu na wygodniejszą formę wydawania poleceń
i wprowadzania danych. Tak oto spotykamy się ze sprzecznością wymagającą kompromisu:
każda z propozycji okazuje się maksymalizować jedno kryterium przy minimalizowaniu dru-
giego. Należy zatem zdecydować, któremu z kryteriów przypisuje się większą wagę (wei ght).
Argument to opinia wyrażona przez osobę, zgadzającą się lub nie z propozycją, kryte-
rium czy oceną. Argumenty są odzwierciedleniem debaty poświęconej poszukiwaniu rozwią-
zań; definiują adekwatność miary dobroci, a czasem prowadzą do podejmowania decyzji. Na
diagramach UML argumenty reprezentowane są przez instancje klasy Argument, posiadającej
atrybuty subject i descri ption. Argumenty powiązane są z encją, której dotyczą, za pomocą
skojarzeń i s supported lub is opposed, zależnie od tego, czy przemawiają na jej rzecz, czy
przeciwko niej.
560 Rozdział 12. • Zarządzanie racjonalizacją

a va i l a b i l i ty$ : C r i t e r i on System powinien być dostępny w co najmniej 99%.

usability!:Criterion Wydanie polecenia nie powinno zajmować więcej n i ż 2 sekundy.

Rysunek 12.5. Przykład oceny propozycji przez kryteria. Kryteria wyróżnione zostały pogrubioną
ramką. Spełnienie kryterium przez propozycję reprezentowane jest przez etykietę meets skojarzenia
assesment, niespełnienie — przez etykietę fai 1 s

Stając wobec wyboru między dostępnością a użytecznością systemu CTC, zauważamy,


że korzyści z większej użyteczności systemu mogą być okupione uszczerbkiem na jego do-
stępności, co może się przekładać na poważne awarie niwelujące korzyści wynikające z wy-
godniejszej i sprawniejszej obsługi systemu — jest to argument przemawiający zarówno na
rzecz kryterium dostępności (avai labil i ty), jak i przeciwko interfejsowi GUI (poi nt&cl ick).
Relację tę przedstawiliśmy na rysunku 12.6.
Definiując kryteria, oceniając propozycje, argumentując „za" i „przeciw", dokonujemy
wartościowania elementów w przestrzeni rozwiązań. Kolejnym krokiem jest wybór jednego
z tych rozwiązań w celu rozstrzygnięcia problemu i zamknięcia zagadnienia.

12.3.5. Kolapsacja przestrzeni rozwiązań: rozstrzygnięcie


Rozstrzygnięcie (resolution) reprezentuje ewentualność wybraną jako sposób zamknięcia za-
gadnienia. Decyzja rozstrzygnięcia wpływa na jeden z modeli systemu lub model zadań. Roz-
strzygnięcie jest wynikiem rozpatrywania wielu propozycji i jednocześnie podsumowaniem
ich uzasadnienia. Na diagramach UML rozstrzygnięcia reprezentowane są przez instancje klasy
Resolution, definiującej atrybuty s u b j e c t , d e s c r i p t i o n , j u s t i f i c a t i o n i s t a t u s . Każde
rozstrzygnięcie, jako obiekt klasy Resol ution, powiązane jest z odnośnymi propozycjami
(jako obiektami klasy Proposal) za pomocą skojarzeń based-on. Jednocześnie każde rozstrzy-
gnięcie powiązane jest za pomocą skojarzenia resol ves z dokładnie jednym zagadnieniem
(jako obiektem klasy Issue), dla którego jest rozwiązaniem.
Nieustanne zmiany w projekcie mogą powodować, że rozstrzygnięcie, które aktualnie
jest rozwiązaniem pewnego zagadnienia, przestanie nim być, gdy zamknięte (closed) zagad-
nienie zostanie ponownie otwarte (open). Z tą okolicznością wiąże się atrybut s t a t u s klasy
Resol ution; gdy odnośne zagadnienie jest zamknięte, czyli gdy rozstrzygnięcie pozostaje jego
12.3. Koncepcje racjonalizacji 561

availability- Interfejs „wskaż i kliknij" jest bardziej skomplikowany w implementacji


first!:Argument niż interfejs tekstowy. Jest także trudniejszy w testowaniu, bowiem
znacznie szerszy jest repertuar akcji, jakie może wykonywać dyspozytor.
Wiąże się więc z ryzykiem wprowadzenia do systemu fatalnych błędów,
których konsekwencje mogą całkowicie zniwelować wszelkie korzyści
wynikające z wygodniejszej obsługi.

Rysunek 12.6. Przykład argumentu (reprezentujący go obiekt został wyróżniony pogrubioną ramką)

rozwiązaniem, atrybut status ma wartość act i ve; w przeciwnym razie ma on wartość obsol ete.
Z zamkniętym zagadnieniem skojarzone jest dokładnie jedno rozstrzygnięcie o statusie act i ve
i dowolna liczba (lub zero) rozstrzygnięć o statusie obsol ete.
Ostatecznie więc zdecydowaliśmy się na wybór tekstowego interfejsu dla naszego sys-
temu CTC. Decyzja taka podyktowana została większym priorytetem kryterium dostępności
w stosunku do kryterium użyteczności; w konsekwencji dyspozytor nie będzie mógł oglądać
tak dużej ilości danych jednocześnie jak w przypadku interfejsu GUI, a wprowadzanie danych
za pomocą klawiatury będzie trwało dłużej i będzie mniej wygodne w porównaniu z klikaniem.
W zamian jednak zyskujemy większą pewność funkcjonowania całego systemu. Na diagramie
U M L (patrz rysunek 12.7) wybrane przez nas rozwiązanie reprezentowane jest jako obiekt
klasy Resol uti on skojarzony z dwoma obiektami klasy Issue.
Dodanie rozstrzygnięcia do modelu zagadnień oznacza formalne zakończenie dyskusji
nad danym zagadnieniem. Wobec iteratywnego charakteru procesu tworzenia systemu mogą
pojawić się potrzeby otwarcia któregoś z zamkniętych zagadnień i ponownego ocenienia od-
rzuconych ewentualności. Gdy jednak proces ten zostanie zakończony, większość zagadnień
musi być zamknięta, a otwarte powinny być jawnie opisane w dokumentacji w postaci listy
znanych problemów.

12.3.6. Implementowanie rozstrzygnięć: elementy działania


Rozstrzygnięcie implementowane jest w postaci elementów działania (action items). Element
działania to zadanie, do którego przydzielona jest osoba odpowiedzialna i dla którego okre-
ślono datę zakończenia. Elementy działania nie są elementami racjonalizacji per se, lecz raczej
562 Rozdział 12. • Zarządzanie racjonalizacją

text-based & Wybraliśmy tekstowy sposób prezentowania danych i wprowadzanie danych


Aboard ; RggP'* u tjgl! za pomocą klawiatury. Odpowiednia emulacja terminala musi zapewnić czytelne
wyświetlanie stanu odcinków izolowanych za pomocą znaków semigraficznych.
Nasza decyzja podyktowana została prostotą i większą niezawodnością interfejsu
tekstowego w porównaniu z interfejsem GUI. Zdajemy sobie sprawę, że jej
podjęcie okupione zostanie mniejszą wygodą w zakresie użytkowania systemu
przez dyspozytora, a wprowadzanie przez niego danych za pomocą klawiatury
będzie wolniejsze i bardziej podatne na błędy.

Rysunek 12.7. Przykład zamkniętego zagadnienia. Jego rozstrzygnięcie wyróżnione jest pogrubioną ramką

częścią modelu zadań (patrz rozdział 14. „Zarządzanie projektem"); mimo to, opisujemy je w tym
miejscu, ponieważ są ściśle powiązane z modelami zagadnień. Na diagramach UML elementy
działania reprezentowane są jako instancje klasy Actionltem definiującej atrybuty subject,
description, owner, deadl ine i status. A t r y b u t owner r e p r e z e n t u j e o s o b ę o d p o w i e d z i a l n ą za
wykonanie zadania związanego z elementem działania, atrybut status może przyjmować cztery
wartości reprezentujące stan wspomnianego zadania: todo („do wykonania"), notDoabl e
(„niewykonalne"), inProgress („trwające") i done („wykonane"). Rozstrzygnięcie powiązane
jest z odnośnym elementem działania poprzez skojarzenie i s imp! emented by. Na rysunku
12.8 widoczny jest element działania generowany przez rozstrzygnięcie z rysunku 12.7.
Zakończyliśmy opis notacji wykorzystywanej do reprezentowania racjonalizacji w po-
staci modelu zagadnień i jego integracji z modelem zadań, przyjrzyjmy się więc funkcjo-
nującym w praktyce wybranym systemom zarządzania racjonalizacją.

12.3.7. Przykłady modeli zagadnień i ich realizacje


Kolekcjonowanie racjonalizacji w postaci modeli zagadnień zostało oryginalnie zaproponowane
przez W. Kunza i H. Rittela. Od tego czasu opracowano wiele różnych modeli na potrzeby inży-
nierii oprogramowania i nie tylko. Opiszemy w skrócie cztery z nich: IBIS (Issue-Based Infor-
mation System [Kunz i Rittel, 1970]), DRL (Decision Representation Language [Lee, 1990]), QOC
(Questions, Options, and Criteria [MacLean i in., 1991]) i NFR Framework [Chung i in., 1999].
12.3. Koncepcje racjonalizacji 563

updateSDD: Act i on Item Dla Alice: uaktualnij SOD w związku z wyborem tekstowego interfejsu.

i nvest i gateTerm: Act i onltem Dla Dave: przeanalizuj możliwości poszczególnych emulacji terminala
tekstowego i ich przydatności do wyświetlania stanu odcinków
w systemie CTC.

Rysunek 12.8. Przykład implementacji rozstrzygnięcia. Element działania wyróżniony jest pogru-
bioną ramką

Issue-Based Information System

Issue-Based Information System (IBIS) to system dedykowany problemom o złej struk-


turze i problemom zagmatwanym, nietypowym (w odróżnieniu od problemów typowych,
„oswojonych"): takich problemów nie sposób „ruszyć" w sposób algorytmiczny, można je
rozwiązywać jedynie w drodze dyskusji i debat.
Model zagadnień systemu IBIS (patrz rysunek 12.9) tworzony jest przez trzy klasy wę-
złów: Issue, Posit i on i Argument kojarzonych ze sobą za pomocą siedmiu różnych klas skoja-
rzeń: supports („wspiera"), objects-to („sprzeciwia się"), repl aces („zastępuje"), responds-to
(„odpowiada"), general izes („uogólnia"), questions („kwestionuje") i suggests („sugeruje").
Węzeł klasy Issue reprezentuje problem projektowy. Propozycje jego rozwiązywania repre-
zentowane są przez węzły klasy Position (podobnej do klasy Proposal opisywanej w sekcji
12.3.3). Węzły klasy Argument reprezentują wartościowanie przez programistów poszczegól-
nych propozycji i powiązane są z węzłami klasy Posi tion za pomocą skojarzeń supports
(„wspiera") i objects-to („sprzeciwia się"). Dany argument (Argument) może być powiązany
z kilkoma propozycjami.
Oryginalny model systemu IBIS nie zawiera węzłów reprezentujących kryteria i roz-
strzygnięcia.

Rysunek 12.9. Model systemu IBIS


564 Rozdział 12. • Zarządzanie racjonalizacją

IBIS wspierany jest przez narzędzie hipertekstowe o nazwie gIBIS, opisane przez J. Con-
klina i K. C. Burgessa-Yakemovica [Conklin i Burgess-Yakemovic, 1991], i wykorzystywany
do kolekcjonowania racjonalizacji w trakcie spotkań osobistych. Stanowi jednocześnie bazę
dla większości późniejszych rozwiązań bazujących na modelach zagadnień, między innymi
DRL i QOC.

Decision Representation Language

System Decision Representation Language (DRL) pomyślany został jako narzędzie


wspomagające kolekcjonowanie racjonalizacji projektowania, rozumianej jako reprezentacja
jakościowych elementów podejmowania decyzji — rozpatrywanych ewentualności rozwiąza-
nia, ich ocen i kształtujących te oceny argumentów oraz kryteriów oceniania. DRL wspierany
jest przez narzędzie o nazwie SYBIL, umożliwiające użytkownikowi śledzenie zależności mię-
dzy elementami racjonalizacji w sytuacji ponownej ewaluacji poszczególnych ewentualności
rozwiązań. DRL stanowi rozwinięcie systemu IBIS, przez wzbogacenie modelu o dwie klasy
węzłów reprezentujące cele projektowe (Goal) i procedury (Procedure). Z perspektywy DRL
konstruowanie racjonalizacji związanej z artefaktem jest zadaniem porównywalnym z two-
rzeniem samego artefaktu. Podstawową wadą DRL jest jego złożoność — siedem klas węzłów
i piętnaście klas skojarzeń, co widać na rysunku 12.10 — oraz dodatkowy wysiłek konieczny
dla nadania racjonalizacji odpowiedniej struktury.

Rysunek 12.10. Model systemu DRL

Questions, Options, and Criteria

Innym efektem rozszerzenia systemu IBIS jest system o nazwie Questions, Options, and
Criteria (QOC) („pytania, opcje i kryteria"). Węzły klasy Questi on („pytania") składają się na
reprezentację rozwiązywanego problemu. Propozycje jego rozwiązań reprezentowane są przez
„opcje", czyli węzły klasy Option (odpowiadającej klasie Proposal opisywanej w sekcji 12.3.3).
12.3. Koncepcje racjonalizacji 565

Opcje mogą generować kolejne pytania; są też oceniane (dodatnio lub ujemnie) według kryte-
riów (Criteria) stanowiących relatywną miarę dobroci definiowaną przez programistów.
Argumenty (Argument) mogą wspierać lub kwestionować pytania, opcje i kryteria oraz skoja-
rzenia między nimi. Model systemu QOC przedstawiony jest schematycznie na rysunku 12.11.

Rysunek 12.11. Model systemu Q O C

QOC i IBIS różnią się jednak zasadniczo na poziomie procesów. Zadaniem systemu
IBIS jest kolekcjonowanie elementów racjonalizacji na bieżąco, gdy pojawiają się w toku dys-
kusji i rejestrowane są za pomocą wspomnianego narzędzia gIBIS. W systemie QOC struktura
modelu tworzona jest natomiast jako wynik refleksji nad bieżącym stanem projektu. Koncep-
cyjna separacja faz konstrukcji i argumentacji akcentuje systematyczność i strukturalny cha-
rakter racjonalizacji, w przeciwieństwie do budowania jej „na gorąco". Z perspektywy modelu
QOC racjonalizacja jest opisem eksplorowania przestrzeni projektowej przez programistów,
system IBIS postrzega natomiast racjonalizację jako chronologiczny zapis analizy prowadzącej
do określonego projektu. W praktyce każde z tych podejść okazuje się przydatne do kolekcjo-
nowania racjonalizacji w wystarczającym kształcie. (Aktywnościami związanymi z kolekcjo-
nowaniem racjonalizacji i zarządzaniem nią zajmiemy się w sekcji 12.4).

NFR

Framework NFR3, opisany przez L. Chunga, B. A. Nixona, E. Yu i J. Mylopoulosa [Chung


i in., 1999], w przeciwieństwie do trzech poprzednio omawianych modeli zagadnień dedyko-
wany jest inżynierii wymagań, udostępnia bowiem metody śledzenia wymagań pozafunkcyj-
nych związanych z poszczególnymi decyzjami, ocenianych propozycji rozwiązań i interakcji
między wspomnianymi wymaganiami. Wymagania pozafunkcyjne traktowane są jako cele do
osiągnięcia; jako że wymagania pozafunkcyjne są ze swej natury wysokopoziomowe i subiek-
tywne — a przez to trudno się nimi operuje na poziomie modeli zagadnień — wspomniane
cele mogą być doskonalone przez podział na podcele. Cele i podcele reprezentowane są w mo-
delu jako węzły grafu, relacje dekompozycji na podcele reprezentowane są przez skierowane
krawędzie tego grafu. Model NFR dostarcza dwa typy dekompozycji. Oto one.

3
Skrót od Non-Functional-Requirements — „wymagania pozafunkcyjne" — przyp. tłum.
566 Rozdział 12. • Zarządzanie racjonalizacją

• Dekompozycja koniunkcyjna (AND decomposition) — by spełniony był określony cel,


muszą być spełnione wszystkie podcele stanowiące wynik jego dekompozycji.
• Dekompozycja dysjunkcyjna (OR decomposition) — by spełniony był określony cel,
wystarczy spełnienie co najmniej jednego spośród podcelów stanowiących wynik jego
dekompozycji.

Tak więc cele wysokopoziomowe (specyfikowane przez klienta i użytkowników) dekom -


ponowane są na bardziej szczegółowe podcele. Co ciekawe, nie musi to być podział ściśle drze-
wiasty — jeden podcel może być podporządkowany kilku celom macierzystym. NFR udostęp-
nia ponadto nowe klasy skojarzeń do reprezentowania rozmaitych powiązań, przykładowo
dany cel może wspierać inny cel lub kolidować z nim. Ponieważ wymagania pozafunkcyjne
rzadko mają postać umożliwiającą kwalifikowanie binarne („spełnione-niespełnione"), skoja-
rzenia między celami (podcelami) reprezentują także stopień, w jakim jeden cel koresponduje
z drugim lub z nim koliduje. Cel uważa się za „usatysfakcjonowany" (satisficed, nie mylić
z satisfied — „spełniony")4, jeśli wybrana ewentualność spełnia go w ramach minimum celów
wspierających wyznaczonego przez skojarzenie; w przeciwnym razie cel uważany jest za
„stłamszony" (denied). Ów limit formułowany jest w postaci pięciu następujących wag przypi-
sywanych skojarzeniu: makes („powoduje"), helps („wspomaga"), neutral („nie ma związku"),
hurts („kłóci się") i breaks („zaprzecza").
Węzły najwyższego poziomu reprezentują cele wysokopoziomowe specyfikowane
przez klienta. W wyniku ich sukcesywnego doskonalenia do postaci celów bardziej szcze-
gółowych uzyskujemy zbiór celów reprezentujących poszczególne cechy systemu — cele te
nazywane są celami realizacyjnymi (operationalizing), dla odróżnienia od celów repre-
zentujących oryginalne wymagania pozafunkcyjne.
Na rysunku 12.12 widoczny jest fragment grafu ukazujący warianty mechanizmu uwie-
rzytelniania transakcji bankomatowych. Celami wysokopoziomowymi są tu: elastyczność
(Flexibil ity), niski koszt (Low cost) i bezpieczeństwo (security). Ogólnie pojęte bezpie-
czeństwo dekomponowane jest koniunkcyjnie na składowe: uwierzytelnienie (Authenti cati on),
poufność (Confidential ity) i integralność (i ntegri ty) — bezpieczny system może zezwalać
na dostęp do kont jedynie uprawnionym użytkownikom, dokonywane transakcje muszą być
tajne, a każda transakcja musi zakończyć się z zachowaniem integralności stanu kont (awaria
systemu nie może spowodować zagubienia ani wykreowania kwoty). Uwierzytelnienie (jako
cel) jest dalej dekomponowane dysjunkcyjnie ze względu na środki uwierzytelnienia, którymi
mogą być: numer konta wraz z numerem PIN (Account+PIN), karta inteligentna wraz z nu-
merem PIN (SmartCard+PIN) lub linie papilarne (FingerPrint). Każdy z tych trzech ostat-
nich podcelów jest celem realizacyjnym, na diagramie został więc wyróżniony pogrubioną
obwódką. Każdy z nich pozostaje w określonej relacji z innymi celami; na diagramie ograni-
czyliśmy się do pokazania, jak ma się uwierzytelnianie za pomocą numerów konta i PIN oraz
uwierzytelnianie za pomocą linii papilarnych do wymagań elastyczności i niskiego kosztu.

4
„Spełnienie" to właściwość kategoryczna: cel może być spełniony albo nie. „Usatysfakcjonowanie"
to wielkość mierzalna: dany cel jest tym bardziej „usatysfakcjonowany", im więcej podcelów jest z nim
zgodnych. Odpowiednio duży wskaźnik usatysfakcjonowania jest więc „rozmytym" odpowiednikiem
spełnienia — przyp. tłum.
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 567

Rysunek 12.12. Dekompozycja celów projektowych w ramach frameworku NFR na przykładzie me-
chanizmu uwierzytelniania transakcji bankomatowych

Gdy w wyniku kompozycji zidentyfikowanych zostanie kilka celów realizacyjnych, pro-


gramiści wybierają i wartościują wybrane ich podzbiory; na podstawie skojarzeń między ce-
lami można wówczas wyrokować o „usatysfakcjonowaniu" lub „stłamszeniu" poszczególnych
celów wysokopoziomowych. W przykładzie na rysunku 12.12 jako rozwiązanie wybrany został
pierwszy z wymienionych środków uwierzytelnienia (Account+PIN); jak widzimy, „usatysfak-
cjonowane" zostały cele Authenti cat i on i Low cost, zaś „usatysfakcjonowanie" celu Securi ty
uzależnione jest od „usatysfakcjonowania" celów Confidential i ty i I n t e g r i t y . Nie jest na-
tomiast „usatysfakcjonowany" cel FI ex i bi 1 i ty.
Dla typowego, niebanalnego systemu przedstawiony graf dekompozycji rozrasta się
i komplikuje do tego stopnia, że do oceniania i porównywania różnych ewentualności roz-
wiązania konieczne jest użycie narzędzi wspomagających. Jedną z zalet frameworku NFR,
której nie będziemy szczegółowo opisywać, jest możliwość wielokrotnego wykorzystywania
dokonanej dekompozycji; umożliwia to programistom grupowanie wyników poprzednich
eksploracji i ponowne ich wartościowanie w świetle nowych kryteriów.

12.4. Aktywności racjonalizacji — od zagadnień do decyzji


Właściwe zarządzanie racjonalizacją ułatwia programistom radzenie sobie z koniecznymi
zmianami. Dokumentując uzasadnienie każdej decyzji, mogą oni łatwiej przeanalizować podjęte
wcześniej istotne decyzje pod kątem ich aktualności w obliczu nowych wymagań klienta czy
zmian w środowisku. By jednak modele racjonalizacji mogły być rzeczywiście użyteczne, za-
warta w nich informacja musi być aktualna, musi posiadać odpowiednią strukturę i musi być
łatwo dostępna. W tej sekcji opiszemy związane z tym aktywności, czyli:

• kolekcjonowanie racjonalizacji w ramach zebrań (patrz sekcja 12.4.2),


• rewidowanie modeli racjonalizacji i ich sukcesywne ulepszanie (patrz sekcja 12.4.3),
• wzbogacanie racjonalizacji o nowe elementy podczas wprowadzania zmian do systemu
(patrz sekcja 12.4.4),
• rekonstruowanie racjonalizacji ukrywającej się za modelami systemu (patrz sekcja
12.4.5).
568 Rozdział 12. • Zarządzanie racjonalizacją

Najbardziej krytyczne elementy racjonalizacji rodzą się na etapie projektowania systemu:


decyzje podejmowane na tym etapie mają wpływ na każdy podsystem i ich zmiana jest zwykle
kosztowna, szczególnie gdy odbywa się w późnych stadiach procesu. Co więcej, racjonalizacja
kryjąca się za dekompozycją systemu jest zazwyczaj bardzo skomplikowana, jako że rozciąga
się na dużą liczbę zagadnień, takich jak odwzorowanie w węzły sprzętowe, przechowywa-
nie trwałych danych, kontrola dostępu, globalne sterowanie przepływem i warunki graniczne
(o czym pisaliśmy w rozdziale 7. „Projekt systemu: realizacja celów projektowych").
Z tych właśnie względów w tym rozdziale skoncentrujemy się właśnie na etapie pro-
jektowania systemu; opisywane techniki kolekcjonowania racjonalizacji wyglądają podobnie
na wszystkich innych etapach, począwszy od zbierania wymagań, aż po testy pilotażowe. Po-
szczególne aktywności opisywać będziemy w kontekście realnego przykładu, jakim jest system
CTC; rozpocznijmy więc od przedstawienia projektu tego systemu.

12.4.1. Projekt systemu CTC


Ogólną charakterystykę systemu CTC przedstawiliśmy w sekcji 12.3.1. Wyobraźmy sobie, że
uczestniczymy w procesie inżynierii wtórnej, czyli w unowocześnianiu tego systemu, polega-
jącym na zastąpieniu centralnego komputera mainframe siecią stacji roboczych. Zamierzamy
także rozszerzyć funkcje systemu o takie mechanizmy jak kontrola dostępu i generalnie lepsze
wsparcie dla bezpieczeństwa i użyteczności. Znajdujemy się właśnie w środku etapu projek-
towania systemu i na razie sformułowaliśmy (na podstawie wymagań pozafunkcyj nych) kilka
celów projektowych (które wymieniamy, począwszy od najważniejszych).

• Dostępność. Awarie systemu nie mogą się zdarzać częściej niż jedna na miesiąc,
a powrót systemu do normalnej pracy po awarii nie powinien trwać dłużej niż 10
minut.
• Bezpieczeństwo. Żadna encja zlokalizowana poza nastawniami nie może uzyskiwać
dostępu do informacji o stanie zarządzanych odcinków izolowanych ani też nie może
mieć możliwości manipulowania jakimikolwiek urządzeniami.
• Użyteczność. Przeszkolony dyspozytor może pomyłkowo wydawać błędne polecenia
nie częściej niż dwa razy dziennie.

Każdy dyspozytor posiada własny węzeł kliencki, zaś całością systemu zarządza para du-
blujących się serwerów (patrz rysunek 12.13 i tabela 12.1). Serwery te odpowiedzialne są także
za przechowywanie trwałych danych; dane te przechowywane są w formie niezależnych plików,
które można kopiować offline i następnie importować do bazy danych w celu przetwarzania
ich zawartości. Komunikacja systemu z urządzeniami (stacjami przydrożnymi) odbywa się za
pośrednictwem modemów zarządzanych przez dedykowaną maszynę. Oprogramowanie midd-
leware realizuje dwa rodzaje komunikacji między podsystemami: wywoływanie metod reali-
zujących żądania oraz powiadamianie o zdarzeniach istotnych dla poszczególnych podsystemów.
Przed nami zadanie rozwiązania zagadnień związanych z kontrolą dostępu oraz mechanizma-
mi zabezpieczającymi dyspozytorów przed ingerowaniem w ich sekcje dyspozycyjne przez
innych dyspozytorów.
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 569

Rysunek 12.13. Dekompozycja systemu CTC. Zarządzanie stanem systemu jest zadaniem serwera
mai nServer, który w razie awarii zastępowany jest przez serwer hotBackup. Serwer wysyła polecenie i odbiera
informację o stanie odcinków za pośrednictwem modemów, którymi zarządza węzeł ModemPool

Tabela 12.1. Opis podsystemów systemu CTC z rysunku 12.13

DispatcherClient Każdemu dyspozytorowi przydzielony jest węzeł D i s p a t c h e r C l i e n t ,


realizujący interfejs użytkownika.
CTCServer Zarządza stanem systemu: wysyła dane do urządzeń w terenie i odbiera informacje
0 ich stanie za p o ś r e d n i c t w e m węzła ModemPool. Odpowiedzialny także
za przechowywanie trwałych danych (między innymi adresów sieciowych
1 nazw urządzeń, przydziału dyspozytorów do sekcji dyspozycyjnych,
rozkładów jazdy pociągów i tym podobnych). Redundancja w postaci pracy
dublujących się serwerów m a na celu zwiększenie dostępności systemu.
ModemPool Zarządza modemami realizującymi komunikację z urządzeniami w terenie.

ModemManager Zarządza połączeniami z urządzeniami w terenie i transmituje do nich polecenia.

StorageSubsystem Zarządza stanem trwałych danych systemu.


Tracki ngSubsystem Zarządza stanem poszczególnych odcinków izolowanych, na podstawie
informacji uzyskiwanych od urządzeń w terenie. Wysyła (za pośrednictwem
węzła ModemManager) polecenia do urządzeń w terenie, bazując na poleceniach
otrzymywanych od węzła Ul Subsystem.
Ul Subsystem Odpowiedzialny za wyświetlanie stanu odcinków izolowanych na użytek
dyspozytora oraz kontrolowanie poprawności poleceń wydawanych przez
dyspozytora przed przekazaniem ich do węzła CTCServer.

12.4.2. Kolekcjonowanie racjonalizacji w ramach zebrań


Z e b r a n i a u m o ż l i w i a j ą p r o g r a m i s t o m p r e z e n t o w a n i e , n e g o c j o w a n i e i r o z w i ą z y w a n i e zagad-
n i e ń w w a r u n k a c h o s o b i s t e j k o m u n i k a c j i . F i z y c z n a o b e c n o ś ć d y s k u t a n t ó w jest t u b a r d z o
ważna, dostarcza b o w i e m sygnały komunikacji niewerbalnej, w postaci oceny nastawienia
i n n y c h u c z e s t n i k ó w d o p r o b l e m u i ich m o t y w a c j i . N e g o c j o w a n i e i p o d e j m o w a n i e decyzji „ n a
570 Rozdział 12. • Zarządzanie racjonalizacją

odległość", na przykład ze pośrednictwem e-maili, jest mniej efektywne, bo łatwiej wówczas


0 nieporozumienia, tak więc spotkania „twarzą w twarz" stanowią bardziej naturalny punkt
wyjścia do kolekcjonowania racjonalizacji.
Procedury organizowania zebrań i związane z nimi dokumenty (agendy i protokoły) opi-
saliśmy już w rozdziale 3. „Organizacja projektu i komunikacja". Agenda, publikowana przed
zebraniem, opisuje status problemu i jego aspekty przeznaczone do przedyskutowania. Przebieg
zebrania dokumentowany jest w formie protokołu, który opublikowany zostaje wkrótce po
zakończeniu zebrania. Wykorzystując koncepcje modelowania zagadnień opisane w sekcji 12.3,
tworzymy agendę w kategoriach zagadnień, na temat których chcemy dyskutować i rozwiązania
których chcemy poszukiwać. Jako cel zebrania określamy rozwiązanie tychże zagadnień, a także
wszystkich innych podzagadnień, które wynikną w trakcie dyskusji. Protokołowi zebrania na-
dajemy strukturę uwzględniającą propozycje zgłaszane w trakcie zebrania, uzgodnione kryteria
ich oceniania oraz argumenty wysuwane na poparcie poszczególnych propozycji i przeciwko
nim. Podjęte decyzje formułujemy w kategoriach rozstrzygnięć i implementujących je ele-
mentów działania. Przegląd statusu tych ostatnich będzie prawdopodobnie jednym z punktów
następnych zebrań.
Jako przykład rozpatrzmy zagadnienia związane z kontrolą dostępu w systemie CTC.
Musimy w związku z tym zorganizować spotkanie z udziałem zespołu architektonicznego
oraz programistów odpowiedzialnych za podsystemy UISubsystem, Tracki ngSubsystem
1 Noti f i cati onServi ce. Alice, facilitator zespołu architektonicznego, publikuje poniższą agendę.

AGENDA: Integracja kontroli dostępu i powiadamiania


Gdzie i kiedy Role
Data: 13 września Pierwszy facilitator: Alice
Początek: 16:30 Chronometrażysta: Dave
Koniec: 17:30 Protokolant: Ed
Pokój: Train Hall, 3420

1. Cel
Pierwszą rewizję projektów odwzorowania sprzętowo-programowego i systemu przechowywania
danych m a m y już za sobą, konieczne jest teraz zdefiniowanie m o d e l u kontroli dostępu i jego
integracji z istniejącymi podsystemami, między innymi Noti f i c a t i onServi ce i Tracki ngSubsystem.

2. Oczekiwane wyniki
Rozwiązanie zagadnień związanych z integracją kontroli dostępu i powiadamiania.
3. Rozpowszechniana informacja [czas: 15 minutl
Al [1]: Dave: Analiza modelu kontroli dostępu realizowanej przez middleware.
4. Dyskusja [czas: 35 minut]
I [1]: Czy dyspozytor ma prawo oglądać sekcje dyspozycyjne kontrolowane przez innych dyspozytorów?
I [2] :Czy dyspozytor ma prawo wysyłania poleceń do urządzeń znajdujących się w sekcjach
dyspozycyjnych k o n t r o l o w a n y c h przez innych dyspozytorów?
I [3]: W jaki sposób kontrola dostępu powinna być zintegrowana z podsystemami Tracki ngSubsystem
i NotificationService?
5. Podsumowanie [czas: 5 minut]
Przegląd i przyporządkowanie nowych elementów działania.
Uwagi krytyczne
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 571

W czasie zebrania dyskutowany będzie element działania stanowiący wynik poprzed-


niego zebrania zespołu architektonicznego: AI[1]: Analiza modelu kontroli dostępu realizo-
wanej przez middleware. Middleware dostarcza podstawowe bloki dla uwierzytelniania i szy-
frowania informacji, nie wnosi nic poza tym do modelu kontroli dostępu. Zagadnienia I [1]
i I [2] zostaną rozwiązane dość szybko w oparciu o wiedzę z dziedziny aplikacyjnej: każdy
dyspozytor może oglądać wszystkie sekcje dyspozycyjne, lecz możliwość manipulowania urzą-
dzeniami ograniczona jest do jego własnej sekcji. Zagadnienie I [3] (W jaki sposób kontrola do-
stępu powinna być zintegrowana z podsystemami Tracki ngSubsystem i Noti f i cati onServi ce?)
nie jest jednak tak oczywiste i stanowić będzie temat do dyskusji.
Trwa zebranie. Dave, programista odpowiedzialny za komponent Noti f i cati onServi ce,
proponuje zintegrowanie kontroli dostępu z klasą TrackSection (patrz rysunek 12.14 i tabela
12.2). Klasa TrackSecti on powinna w związku z tym utrzymywać listę uprawnień, określającą
uprawnienia poszczególnych dyspozytorów do odczytywania i modyfikowania stanu poszcze-
gólnych sekcji dyspozycyjnych. Podsystem zainteresowany zdarzeniami zachodzącymi
w ramach klasy TrackSection powinien subskrybować powiadamianie o tych zdarzeniach za
pomocą usługi Noti f i c a t i onServi ce. Usługa ta powinna sprawdzać, czy dyspozytor żą-
dający informacji o danej sekcji dyspozycyjnej ma uprawnienia do (przynajmniej) odczyty-
wania tego stanu.

Rysunek 12.14. Propozycja P [ l ] : Dostęp do klasy TrackSection kontrolowany jest za pomocą listy
uprawnień utrzymywanej przez ten komponent. W oparciu o tę listę usługa Noti f i c a t i onServi ce
sprawdza, czy żądający informacji dyspozytor ma prawo do jej uzyskiwania

Alice, programistka odpowiedzialna za podsystem TrackSubsystem, którego częścią jest


klasa TrackSection, proponuje odwrócenie zależności między klasą TrackSection a usługą
Noti f i cati onServi ce (patrz rysunek 12.15 i tabela 12.3). Zgodnie z jej propozycją, podsys-
tem Ul Cl ient nie powinien mieć bezpośredniego dostępu do usługi Noti f i cati onServi ce,
nawet przy subskrybowaniu informacji. W celu dokonania subskrypcji podsystem Ul Cl ient
wywoła metodę subscribeToEvents() klasy TrackSection, która to metoda po pozytyw-
nym zweryfikowaniu uprawnień wywoła operację subscri beToStateChangeEvents () usługi
Noti f i c a t i onServi ce. Dzięki temu uzyskamy scentralizowanie w ramach jednej klasy
572 Rozdział 12. • Zarządzanie racjonalizacją

Tabela 12.2. Opis elementów modelu widocznego na rysunku 12.14

Noti f i c a t i onServi ce Usługa propagująca informację o zmianie stanu sekcji dyspozycyjnej


(TrackSecti on). W celu otrzymywania informacji dotyczącej określonej
sekcji za pośrednictwem tej usługi zainteresowany podsystem powinien tę
informację subskrybować. Może to jednak uczynić tylko podsystem
posiadający u p r a w n i e n i a do odczytywania i n f o r m a c j i na temat
wspomnianej sekcji. Uprawnienia te odczytywane są za pomocą operacji
i sAccessi bl e ( ) klasy TrackSecti on.

System System odpowiedzialny za bezpieczne sprawdzanie, na podstawie


informacji przekazanej przez k o m p o n e n t Ul Cl i ent, który dyspozytor
żąda informacji. Obiekt TrackSecti on weryfikuje uprawnienia bieżącego
dyspozytora za pomocą operacji whoIsThis().
TrackSecti on Klasa r e p r e z e n t u j ą c a sekcję dyspozycyjną, złożoną z o d c i n k ó w
reprezentowanych przez obiekty TrackCi rcui t s i powiązane z nimi
obiekty Devi ces. Na poziomie klasy TrackSecti on realizowana jest
kontrola dostępu, w oparciu o listę utrzymywaną przez każdy obiekt
tej klasy.
UI Cl i e n t Podsystem odpowiedzialny za wyświetlanie stanu sekcji dyspozycyjnych
i przyjmowanie poleceń od dyspozytora.

Rysunek 12.15. Propozycja P[2]: klient zostaje pozbawiony dostępu do usługi Noti f i c a t i onServi ce,
całość operacji związanych z kontrolą dostępu skupiona zostaje w klasie TrackSecti on, za pośrednictwem
której klient subskrybuje żądaną informację

wszystkich „ c h r o n i o n y c h " 5 operacji i całokształtu k o n t r o l i d o s t ę p u . P o n a d t o , g d y z m i e n i ą się


u p r a w n i e n i a d o s t ę p u , klasa T r a c k S e c t i on m o ż e w e w ł a s n y m zakresie a n u l o w a ć s u b s k r y p c j e
pozostające w sprzeczności z n o w y m i uprawnieniami.

5
Autorzy używają tu przymiotnika „chroniona" (protected) na określenie faktu, że wykonanie danej
operacji uzależnione jest od uprawnień podsystemu wywołującego tę operację, czyli poprzedzone
weryfikacją tych uprawnień (operacja „chroniona" jest przez mechanizm kontroli dostępu). Nie należy
więc tym razem kojarzyć słowa „chroniona" z innym jego znaczeniem — poziomem widzialności
p r o t e c t e d elementu klasy (patrz sekcja 9.3.2) — przyp. tłum.
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 573

Tabela 12.3. Opis elementów modelu widocznego na rysunku 12.15; zmiany w stosunku do tabeli
12.2 wyróżnione zostały kursywę

Noti f i c a t i onServi ce Usługa propagująca informację o zmianie stanu sekcji dyspozycyjnej


(TrackSecti on). W celu otrzymywania informacji dotyczącej określonej
sekcji za pośrednictwem tej usługi zainteresowany podsystem powinien tę
informację subskrybować. Może to jednak uczynić tylko podsystem
posiadający uprawnienia do odczytywania informacji na temat
wspomnianej sekcji. Uprawnienia te odczytywane są za p o m o c ą
operacji i sAccessi bl e ( ) klasy T r a c k S e c t i o n . Usługa dostępna jest
bezpośrednio jedynie dla klasy T r a c k S e c t i o n , podsystem UIC1 i e n t
nie ma do niej dostępu, subskrybuje on żądaną informację przez
wywoływanie metody s u b s c r i beToEvents () klasy TrackSection.

TrackSecti on Klasa reprezentująca sekcję dyspozycyjną, złożoną z o d c i n k ó w


reprezentowanych przez obiekty T r a c k C i r c u i t s i powiązane z nimi
obiekty Devi ces. Na poziomie klasy TrackSecti on realizowana jest
kontrola dostępu, w oparciu listę utrzymywaną przez każdy obiekt tej
klasy. Klient, żądający informacji o stanie sekcji powinien tę informację
subskrybować, wywołując metodę s u b s c r i beToEvents () klasy
TrackSection. Klasa TrackSecti on po pozytywnym zweryfikowaniu
uprawnień podsystemu żądającego informacji wywołuje operację
s u b s c r i b e T o T r a c k S e c t i o n E v e n t s ( ) usługi N o t i f i c a t i o n S e r v i c e .

Propozycja, k t ó r ą zgłosił Ed, opiera się n a w a ż n y m spostrzeżeniu, iż k o n t r o l i d o s t ę p u p o -


w i n n y podlegać jedynie p r ó b y m o d y f i k o w a n i a stanu sekcji dyspozycyjnych, ponieważ z założenia
o g l ą d a n i e s t a n u wszystkich sekcji m a być d o s t ę p n e dla wszystkich d y s p o z y t o r ó w . Z a k ł a d a j ą c ,
że m o d y f i k a c j a s t a n u sekcji d o k o n u j e się wyłącznie p r z e z w y w o ł y w a n i e o d p o w i e d n i c h m e t o d ,
i b i o r ą c p o d u w a g ę fakt, że usługa Noti f i c a t i o n S e r v i ce w y k o r z y s t y w a n a jest jedynie d o p o -
w i a d a m i a n i a o z m i a n a c h stanu, a n i e uczestniczy w m o d y f i k a c j i w s p o m n i a n e g o s t a n u , ł a t w o
zauważyć, iż u s ł u g a ta n i e p o z o s t a j e w ż a d n e j relacji d o m e c h a n i z m u k o n t r o l i d o s t ę p u . N i e
t r z e b a z a t e m i n t e g r o w a ć jej z t y m m e c h a n i z m e m . O t r z y m u j e m y t y m s a m y m p r o p o z y c j ę bę-
d ą c ą u l e p s z e n i e m p r o p o z y c j i D a v e ' a (patrz r y s u n e k 12.16 i tabela 12.4).

Rysunek 12.16. Propozycja P[3]: jedynie próby modyfikowania stanu obiektu TrackSection podlegają
kontroli dostępu, więc usługa Noti f i c a t i onServi ce przestaje być zależna od tego mechanizmu
574 Rozdział 12. • Zarządzanie racjonalizacją

Tabela 12.4. Opis elementów modelu widocznego na rysunku 12.16; zmiany w stosunku do tabeli 12.2
wyróżnione zo

Noti f i c a t i o n S e r v i ce Usługa propagująca informację o zmianie stanu sekcji dyspozycyjnej


( T r a c k S e c t i on). W celu otrzymywania i n f o r m a c j i dotyczącej
określonej sekcji za p o ś r e d n i c t w e m tej usługi zainteresowany
podsystem powinien tę informację subskrybować. Może to jednak

są za pomocą operacji i sAccessib1-c() klasy TrackSecti ofk

Z e s p ó ł a r c h i t e k t o n i c z n y z d e c y d o w a ł o w y b o r z e p r o p o z y c j i E d a , dzięki jej p r o s t o c i e .
Ed jako protokolant sporządza przedstawiony poniżej chronologiczny protokół.

CHRONOLOGICZNY PROTOKÓŁ: Integracja kontroli dostępu i powiadamiania


Gdzie i kiedy Role
Data: 13 września Pierwszy facilitator: Alice
Początek: 16:30 Chronometrażysta: Dave
Koniec: 18:00 Protokolant: Ed
Pokój: Train Hall, 3420

1. Cel
Pierwszą rewizję projektów odwzorowania sprzętowo-programowego i systemu przechowywania
d a n y c h m a m y już za sobą, konieczne jest teraz zdefiniowanie m o d e l u k o n t r o l i dostępu
i jego integracji z istniejącymi p o d s y s t e m a m i , między i n n y m i Noti f i c a t i onServi ce
i TrackingSubsystem.

2. Oczekiwane wyniki
Rozwiązanie zagadnień związanych z integracją kontroli dostępu i powiadamiania.
3. Rozpowszechniana informacja
Al [1]: Dave: Analiza modelu kontroli dostępu realizowanej przez middleware.
Status: Middleware udostępnia silne mechanizmy szyfrowania i uwierzytelniania, poza
tym nie wprowadza żadnych ograniczeń do modelu dostępu; konieczne jest więc
zaimplementowanie reguł dostępu na serwerze.
4. Dyskusja
I [1]: Czy dyspozytor m a prawo oglądać sekcje dyspozycyjne kontrolowane przez innych
dyspozytorów?
Ed: Tak, wynika to ze specyfikacji CTC.
I [2]: Czy dyspozytor m a prawo wysyłania poleceń do urządzeń znajdujących się w sekcjach
dyspozycyjnych kontrolowanych przez innych dyspozytorów?
Zoe: Nie, tylko dyspozytor przydzielony do d a n e j sekcji dyspozycyjnej m a p r a w o
manipulować urządzeniami tej sekcji. Zauważmy przy tym, że możliwa jest dynamiczna
zmiana przydziału dyspozytorów do sekcji.
Ed: To też wynika ze specyfikacji CTC.
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 575

I [3]: W jaki sposób k o n t r o l a dostępu p o w i n n a być zintegrowana z p o d s y s t e m a m i


Tracki n g S u b s y s t e m i Noti f i c a t i onServi ce?
Dave: W klasie Trac kSect i on zaimplementowana jest lista uprawnień. Usługa powiadamiania
wykorzystuje tę listę do sprawdzania, które podsystemy mają prawo uzyskiwać
informację o stanie tej sekcji.
Alice: P r a w d o p o d o b n i e p o w i n n i ś m y odwrócić k i e r u n e k zależności między usługą
powiadamiania a klasą TrackSecti on. Klient UIC1 i ent nie powinien kontaktować się
b e z p o ś r e d n i o z usługą powiadamiania, lecz tylko z klasą T r a c k S e c t i o n , która
przed udostępnieniem informacji usłudze powiadamiania zweryfikuje uprawnienia
podsystemu żądającego tej informacji. W ten oto sposób skupimy w jednym miejscu
wszystkie mechanizmy zależne od kontroli dostępu.
Dave: Dzięki takiemu rozwiązaniu klasa TrackSecti on będzie mogła automatycznie
zweryfikować istniejące subskrypcje w przypadku zmodyfikowania listy uprawnień.
Ed: Zauważcie, że wszyscy dyspozytorzy mogą oglądać wszystkie sekcje dyspozycyjne,
a więc kontrola dostępu konieczna jest tylko przy próbie modyfikowania ich stanu.
Usługa powiadamiania może więc działać bez kontekstu kontroli dostępu.
Alice: Ale uwzględnienie możliwości powiązania usługi powiadamiającej z kontrolą
dostępu daje bardziej ogólne rozwiązanie.
Ed: Jednocześnie bardziej skomplikowane. Oddzielmy więc w tej chwili powiadamianie
od kontroli dostępu i powróćmy do problemu, gdy zmienią się wymagania.
Alice: Dobrze, muszę zatem zrewidować API podsystemu Tracki n g S u b s y s t e m .
5. Podsumowanie
Al [2]: Alice: Zaprojektowanie kontroli dostępu dla podsystemu Tracki n g S u b s y s t e m w oparciu
o uwierzytelnianie i szyfrowanie udostępniane przez middleware.

E d utworzył swój p r o t o k ó ł p o p r z e z wstawienie d o a g e n d y i n f o r m a c j i o przebiegu dyskusji


dotyczącej poszczególnych zagadnień. Zapis tej dyskusji m a postać chronologicznej listy w y p o -
wiedzi poszczególnych d y s k u t a n t ó w . Większość tych wypowiedzi łączy propozycje i a r g u m e n t y
n a ich rzecz ( o r a z p r z e c i w k o n i e k t ó r y m i n n y m p r o p o z y c j o m ) , a b y więc p r o t o k ó ł był z g o d n y
z m o d e l e m zagadnień, E d n a d a j e m u o d p o w i e d n i ą s t r u k t u r ę , czego efekt w i d o c z n y jest poniżej.

STRUKTURALNY PROTOKÓŁ: Integracja kontroli dostępu i powiadamiania


Gdzie i kiedy Role
Data: 13 września Pierwszy facilitator: Alice
Początek: 16:30 Chronometrażysta: Dave
Koniec: 18:00 Protokolant: Ed
Pokój: Train Hall, 3420
1. Cel
Pierwszą rewizję projektów odwzorowania sprzętowo-programowego i systemu przechowywania
d a n y c h m a m y już za sobą, konieczne jest teraz zdefiniowanie m o d e l u kontroli dostępu
i jego integracji z istniejącymi podsystemami, między i n n y m i Noti f i c a t i onServi ce
i TrackingSubsystem.

2. Oczekiwane wyniki
Rozwiązanie zagadnień związanych z integracją kontroli dostępu i powiadamiania.
576 Rozdział 12. • Zarządzanie racjonalizacją

3. Rozpowszechniana i n f o r m a c j a
Al [1]: Dave: Analiza modelu kontroli dostępu realizowanej przez middleware.
Status: Middleware u d o s t ę p n i a silne m e c h a n i z m y szyfrowania i uwierzytelniania,
poza tym nie wprowadza żadnych ograniczeń do modelu dostępu; konieczne jest
więc z a i m p l e m e n t o w a n i e reguł dostępu na serwerze.

4. Dyskusja
I [1]: Czy dyspozytor ma prawo oglądać sekcje dyspozycyjne kontrolowane przez innych
dyspozytorów?
R [ 1]: Tak (wynika to ze specyfikacji CTC i potwierdzone jest przez Zoe, użytkownika
testującego).
I [2]: Czy dyspozytor ma prawo wysyłania poleceń do urządzeń znajdujących się w sekcjach
dyspozycyjnych kontrolowanych przez innych dyspozytorów?
R[2]: Nie, tylko dyspozytor przydzielony do d a n e j sekcji m a p r a w o m a n i p u l o w a ć
urządzeniami tej sekcji. Zauważmy przy tym, że możliwa jest dynamiczna zmiana
przydziału dyspozytorów do sekcji (wynika to ze specyfikacji CTC i potwierdzone
jest przez Zoe, użytkownika testującego).
I [3]: W jaki sposób kontrola dostępu powinna być zintegrowana z podsystemami
Tracki ngSubsystem i Noti f i c a t i onServi ce?
P [3.1]: W klasie TrackSecti on zaimplementowana jest lista uprawnień. Dany podsystem,
aby subskrybować powiadamianie o zdarzeniach tej sekcji, wysyła żądanie do usługi
powiadamiania, która z kolei k o n t a k t u j e się z p r z e d m i o t o w ą sekcją w celu
zweryfikowania uprawnień wspomnianego podsystemu.
P [3.2]: W klasie TrackSecti on skupione zostają wszystkie chronione operacje. Podsystem
zamierzający subskrybować informacje o stanie danej sekcji, kontaktuje się z tą
właśnie sekcją, która p o zweryfikowaniu jego u p r a w n i e ń wywołuje usługę
p o w i a d a m i a n i a w celu dokonania rzeczonej subskrypcji.
A [3.1] na rzecz P [3.2]: Chronione operacje i kontrola dostępu zostają zintegrowane
w ramach jednej klasy.
P [ 3 . 3 ] : Nie trzeba k o n t r o l o w a ć dostępu związanego z odczytywaniem informacji,
UICLi ent może więc żądać subskrypcji od usługi powiadamiania bez względu
na swe uprawnienia — usługa powiadamiania nie musi w ogóle interesować się
listami uprawnień.
A [3.2] na rzecz P [3.3]: Dyspozytorzy mogą bez ograniczeń oglądać wszystkie sekcje
(patrz R[l]).
A [ 3 . 3 ] na rzecz P [ 3 . 3 ] : Prostota.
R[3]: P [ 3 . 3 ] . Patrz Al [2].
5. Podsumowanie
Al [2]: Alice: Zaprojektowanie kontroli dostępu dla podsystemu Tracki ngSubsystem w oparciu
o uwierzytelnianie i szyfrowanie udostępniane przez middleware, zgodnie z rozstrzygnięciem R[3]
tego protokołu.

R e a s u m u j ą c , r e z u l t a t e m s p o t k a n i a p o ś w i e c o n e g o k o n t r o l i d o s t ę p u b y ł o u s t a l e n i e , że:

® k a ż d y d y s p o z y t o r m o ż e oglądać s t a n wszystkich sekcji, lecz m o d y f i k o w a ć m o ż e w y -


łącznie s t a n s w e j w ł a s n e j sekcji,
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 577

• k o n t r o l a d o s t ę p u o d b y w a się n a p o d s t a w i e listy u p r a w n i e ń s k o j a r z o n e j z k a ż d y m
obiektem klasy T r a c k S e c t i o n ,

• u s ł u g a p o w i a d a m i a n i a (Noti f i c a t i o n S e r v i ce) została o d s e p a r o w a n a o d k o n t r o l i


d o s t ę p u , b o w i e m i n f o r m a c j a o stanie d o w o l n e j sekcji jest d o s t ę p n a dla k a ż d e g o
dyspozytora.

Skupiając się na modelu zagadnień, możemy ponadto zauważyć, że:

• rozważano integrację usługi p o w i a d a m i a n i a (Noti f i c a t i onServi ce) z m e c h a n i z m a m i


kontroli dostępu,

• s c e n t r a l i z o w a n i e w s z y s t k i c h c h r o n i o n y c h m e t o d w klasie T r a c k S e c t i o n b y ł o a k -
ceptowaną regułą.

D w i e ostatnie i n f o r m a c j e są t y p o w y m i i n f o r m a c j a m i w c h o d z ą c y m w skład racjonalizacji;


zazwyczaj i n f o r m a c j e tego t y p u u w a ż a n e są za m a ł o istotne, ich u t r w a l e n i e w m o d e l u o k a z u j e
się j e d n a k k o r z y s t n e w d ł u ż s z e j p e r s p e k t y w i e , g d y p o j a w i a się k o n i e c z n o ś ć z m i a n .

12.4.3. Asynchroniczne kolekcjonowanie racjonalizacji


D y s k u s j e n a zebraniach opierają się o i n f o r m a c j ę k o n t e k s t o w ą . Uczestnicy zebrania d y s p o n u j ą
p o k a ź n y m z a s o b e m w i e d z y n a t e m a t systemu, jego p r z e z n a c z e n i a i p r o j e k t u . Facilitator z k o -
nieczności u k i e r u n k o w u j e p r z e b i e g z e b r a n i a n a w ą s k i zakres z a g a d n i e ń o c z e k u j ą c y c h n a roz-
w i ą z a n i e . I t a k n a p r z y k ł a d w s z y s c y u c z e s t n i c y o p i s y w a n e g o w p o p r z e d n i e j sekcji z e b r a n i a
z p e w n o ś c i ą z n a j ą p r z e z n a c z e n i e s y s t e m u C T C , j e g o e l e m e n t y f u n k c j o n a l n e , cele p r o j e k t o w e
i a k t u a l n y kształt jego d e k o m p o z y c j i n a p o d s y s t e m y . P r o t o k ó ł z zebrania odzwierciedla jedynie
d y s k u t o w a n e n a t y m z e b r a n i u zagadnienia, p o m i j a j ą c cały k o n t e k s t dyskusji. I niestety, w s k u t e k
t e g o k o n t e k s t ó w ulega z c z a s e m z a t r a c e n i u i p r o t o k o ł y stają się c o r a z m n i e j z r o z u m i a ł e .
T e m u właśnie p r o b l e m o w i m o ż n a zaradzić p r z y użyciu m o d e l o w a n i a zagadnień. W roz-
dziale 3. „Organizacja p r o j e k t u i k o m u n i k a c j a " opisywaliśmy wykorzystywanie o p r o g r a m o w a -
nia w s p o m a g a j ą c e g o p r a c ę g r u p o w ą (groupware) w warunkach komunikacji asynchronicznej.
I n t e g r u j ą c p r z y g o t o w a n i e d o z e b r a n i a i jego przebieg z k o m u n i k a c j ą a s y n c h r o n i c z n ą , zysku-
j e m y m o ż l i w o ś ć u t r w a l a n i a także i n f o r m a c j i k o n t e k s t o w e j .
Z a ł ó ż m y n a p r z y k ł a d , że p r o g r a m i s t k a i m i e n i e m M a r y , o d p o w i e d z i a l n a za p o d s y s t e m
UISubsystem, n i e m o g ł a uczestniczyć w z e b r a n i u p o ś w i ę c o n y m k o n t r o l i d o s t ę p u . Z a p o z n a ł a
się z a g e n d ą tego z e b r a n i a i p r o t o k o ł e m ( k t ó r e d o s t ę p n e b y ł y n a g r u p i e d y s k u s y j n e j z e s p o ł u
a r c h i t e k t o n i c z n e g o ) i c h o ć z n a k o m i c i e r o z u m i e w y n i k zebrania, d y s k u s j a n a t e m a t usługi p o -
w i a d a m i a n i a (Noti f i c a t i o n S e r v i ce) w y d a j e się jej nie d o k o ń c a jasna: z g o d n i e z a r g u m e n t e m
A[3.3] dla propozycji P[3.3], p o n i e w a ż każdy dyspozytor m o ż e obserwować stan każdej sekcji,
w s z y s t k i e z d a r z e n i a w s y s t e m i e są p u b l i c z n i e w i d o c z n e , n i e m a w i ę c p o t r z e b y u j m o w a ć
i n f o r m a c j i o n i c h w r a m y m e c h a n i z m u k o n t r o l i d o s t ę p u . A b y j e d n a k k o n t r o l a taka była sku-
t e c z n a w o d n i e s i e n i u d o modyfikowania s t a n u sekcji, k o n i e c z n e jest założenie, że zdarzenia za-
chodzące w jednym podsystemie nie m o g ą powodować zmiany stanu innych podsystemów,
w t y m p o s z c z e g ó l n y c h sekcji T r a c k S e c t i o n . M a r y c h c e się u p e w n i ć , że taki w ł a ś n i e jej t o k
578 Rozdział 12. • Zarządzanie racjonalizacją

m y ś l e n i a jest p r a w i d ł o w y i w z w i ą z k u z t y m wysyła n a g r u p ę d y s k u s y j n ą w i d o c z n y p o n i ż e j
post, w k t ó r y m d o d a t k o w o p r o p o n u j e , b y w k o n s e k w e n c j i z a b r o n i ć u s ł u d z e T r a c k i n g S e r v i c e
subskrybowania jakichkolwiek zdarzeń.

Grupy dyskusyjne: ctc.architecture.discuss

Temat: Data
I[ 1 ]: Czy dyspozytor ma prawo oglądać sekcje dyspozycyjne kontrolowane przez 14 wrz
innych dyspozytorów?
I[2]: Czy dyspozytor ma prawo wysyłania poleceń do urządzeń znajdujących się 14 wrz
w sekcjach dyspozycyjnych kontrolowanych przez innych dyspozytorów?
I[3]: W jaki sposób kontrola dostępu powinna być zintegrowana z podsystemami 14 wrz
TrackingSubsystem i NotificationService?
P[3.1]: W klasie TrackSection zaimplementowana jest lista uprawnień. 14 wrz
P[3.2]: W klasie TrackSection skupione zostają operacje subskrypcyjne. 14 wrz
+A[3.1]: Rozszerzalność. 14 wrz
+A[3.2]: Scentralizowanie wszystkich chronionych operacji. 14 wrz
P[3.3]: Usługa NotificationService nie jest związana z kontekstem kontroli 14 wrz
dostępu.
+A[3.3]: Dyspozytorzy mogą oglądać wszystkie sekcje. 14 wrz
+A[3.3]: Prostota. 14 wrz

Od: Mary
Grupy dyskusyjne: ctc.architecture.discuss
Temat: Zagadnienie wynikowe: Czy dopuszczalne jest realizowanie żądań przy użyciu
powiadamiania?
Data: 15 wrz, czw, 13:12:48 -0400
I[4] w odpowiedzi na A[3.3]: w związku z listami uprawnień w kontekście możliwości:
> Dyspozytorzy mogą oglądać stan wszystkich sekcji, mają więc dostęp do wszystkich zdarzeń.
Z a k ł a d a m y zatem, że stan sekcji TrackSection nie jest sterowany z d a r z e n i a m i i zdarzenia
wykorzystywane są tylko do informowania podsystemów o zmianie stanu w innych podsystemach.
Czy dla większego bezpieczeństwa nie powinniśmy zabronić usłudze TrackingService subskrybowania
jakichkolwiek informacji o zdarzeniach?

W y k o r z y s t y w a n i e tego s a m e g o m o d e l u z a g a d n i e ń z a r ó w n o n a z e b r a n i a c h , j a k i n a p o -
t r z e b y dyskusji o n l i n e u m o ż l i w i a i n t e g r o w a n i e i n f o r m a c j i z w i ą z a n e j z racjonalizacją. M i m o iż
integrację tę z a p e w n i ć m o ż n a p r z y użyciu p r o s t y c h technologii, takich j a k g r u p y d y s k u s y j n e ,
poszczególne elementy — modele zagadnień, agendy oraz protokoły zebrań i związane z n i m i
k o m u n i k a t y — m o g ą być i n t e g r o w a n e także za p o m o c ą z a a w a n s o w a n y c h n a r z ę d z i group ware,
j a k Lotus N o t e s czy w i e l o u ż y t k o w a b a z a z a g a d n i e ń d o s t ę p n a w sieci W W W (czego p r z y k ł a d
w i d z i m y n a r y s u n k u 12.17). U s t a n a w i a j ą c o d p o w i e d n i e p r o c e d u r y w s p o m n i a n e j integracji,
m o ż e m y k o l e k c j o n o w a ć o b s z e r n ą i n f o r m a c j ą z w i ą z a n ą z racjonalizacją. I t u p o j a w i a się k o -
lejne w y z w a n i e — u t r z y m y w a n i e tej i n f o r m a c j i w stanie a k t u a l n y m , s t o s o w n i e d o d o k o n y w a -
nych zmian.
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 579

Rysunek 12.17. Przykład bazy zagadnień — szablon bazy danych (LN IBIS w Domino Lotus Notes).
W tej bazie za pomocą formularzy W W W programiści mogą rejestrować zagadnienia, propozycje,
argumenty i rozstrzygnięcia

12.4.4. Racjonalizacja dyskutowanych zmian


Modele racjonalizacji pomagają programistom uporać się z problemami wynikającymi z ko-
nieczności wprowadzania zmian do systemu; niestety, sama racjonalizacja też jest przed-
miotem zmian, gdy rozważamy adekwatność podjętych decyzji do nowych warunków. Innymi
słowy, projektując rozwiązanie problemu pojawiającego się na przykład w związku ze zmia-
nami w wymaganiach klienta, musimy nie tylko dostarczyć racjonalizacji dla nowych zmian
wprowadzanych do systemu, lecz także powiązać nowe elementy racjonalizacji z tymi, które
kryją się za obecnym kształtem systemu.
Załóżmy dla przykładu, że klient kontraktujący system CTC zmienił swe wymagania
dotyczące kontroli dostępu: dotychczas wszyscy dyspozytorzy mogli bez ograniczenia oglądać
stan wszystkich sekcji, klient uznał jednak, że dla danego dyspozytora interesujący może być
co najwyżej stan sekcji przylegających do jego własnej sekcji — stan innych sekcji jest dla niego
obojętny z punktu widzenia wykonywanych zadań.
Stajemy zatem przed koniecznością zmodyfikowania projektu mechanizmów kontroli
dostępu, w związku z czym organizowane jest zebranie zespołu architektonicznego. Należy
przede wszystkim przeanalizować racjonalizację stojącą za obecnym kształtem wspomnianych
mechanizmów; Alice, główny facilitator zespołu architektonicznego, publikuje widoczną po-
niżej agendę.
580 Rozdział 12. • Zarządzanie racjonalizacją

W trakcie zebrania Dave prezentuje racjonalizację dyskutowaną na poprzednim zebra-


niu i na forum grupy dyskusyjnej zespołu architektonicznego. Zespół architektoniczny po-
twierdza nieaktualność założenia, że dyspozytorzy mogą bez ograniczeń oglądać wszystkie
sekcje — zgodnie z obecnymi wymaganiami możliwość ta ograniczona jest tylko do sekcji
przylegających. Analizując protokół z poprzedniego zebrania, za najbardziej przydatną dla
nowych warunków uznają oni propozycję P[2] (patrz rysunek 12.15): wszystkie chronione
operacje scentralizowane są w klasie TrackSecti on i to ona staje się głównym obiektem
wszelkich zmian wynikających z nowych wymagań dotyczących kontroli dostępu. Niestety,
implementacja istniejącego projektu jest już mocno zaawansowana i przyjęcie propozycji P [2]
jako rozstrzygnięcia wiązałoby się z drastycznymi zmianami istniejącego kodu, czego progra-
miści chcieliby raczej uniknąć. Alicja optuje więc za propozycją P[l] (patrz rysunek 12.14):
nie potrzeba wprowadzać zmian do podsystemu UIC1 i ent, bowiem interfejsy klas TrackSection
i Noti f i cati onServi ce pozostają niezmienione. Zmiany wymaga natomiast implementacja
usługi Noti f i cati onServi ce: w celu weryfikacji praw dostępu usługa ta kontaktuje się z klasą
TrackSection; gdy natomiast wprowadzone zostaną zmiany do listy uprawnień utrzymywanej
przez obiekt klasy TrackSection, ten kontaktuje się z usługą Noti f i cati onServi ce, by anulo-
wać (ewentualne) nieprzysługujące już subskrypcje. Wadą takiego rozwiązania jest cykliczna
zależność obu klas TrackSection i NotificationService, jednak modyfikacja istniejącego
kodu sprowadzona zostaje do rozmiarów minimalnych.
I ta właśnie propozycja wybrana zostaje jako rozstrzygnięcie przez zespół architekto-
niczny. Ed sporządza widoczny poniżej strukturalny protokół (jego chronologiczny pierwo-
wzór darujemy sobie ze względu na oszczędność miejsca).
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 581
582 Rozdział 12. • Zarządzanie racjonalizacją

Protokół ten przydatny jest do dwóch celów: określa racjonalizację nowej zmiany i jed-
nocześnie odnosi ją do istniejącej racjonalizacji. Tę ostatnią przywołuje jako cytat z protokołu
dokumentującego poprzednie zebrania oraz postu na grupie dyskusyjnej zespołu architekto-
nicznego. Wspomniany protokół opublikowany zostanie na rzeczonej grupie dyskusyjnej, by
programiści nieobecni na zebraniu mogli przedyskutować jego treść, zamykając w ten sposób
cykl rejestrowania i wyjaśniania informacji składającej się na racjonalizację. Gdy używane są
narzędzia hipertekstowe, odniesienie do istniejącej racjonalizacji ma formę hiperłączy, co
umożliwia wygodne nawigowanie po powiązanych egzemplarzach informacji.
Nawet jeśli do rejestrowania i śledzenia poszczególnych zagadnień wykorzystywana jest
zaawansowana baza danych, gromadzona w niej informacja może szybko przerodzić się
w ogromny chaos, bez wyraźnej struktury. Co więcej, nie wszystkie zagadnienia są w tej bazie
rejestrowane, bo nie wszystkie są przedmiotem dyskusji na zebraniach: wiele z nich rozstrzy-
ganych jest w ramach nieformalnych konwersacji lub nawet przez pojedynczych programi-
stów, bez jakichkolwiek konsultacji. Konieczne zatem staje się rekonstruowanie umykającej
w ten sposób informacji i integrowanie jej z bieżącą racjonalizacją.

12.4.5. Rekonstruowanie racjonalizacji


Rekonstruowanie racjonalizacji to odmienna metoda jej kolekcjonowania. Zamiast rejestro-
wania na bieżąco, informacja składająca się na racjonalizację jest systematycznie rekonstru-
owana na podstawie modeli systemu, zapisków komunikacyjnych (e-maili, postów na grupach
dyskusyjnych) i faktów zapamiętanych jedynie przez programistów. W ten sposób racjonali-
zacja rozwijana jest w sposób bardziej systematyczny, we wczesnych fazach projektu zu-
żywanych jest mniej zasobów, co umożliwia programistom szybsze uzyskiwanie rozwiązań.
12.4. Aktywności racjonalizacji — od zagadnień do decyzji 583

Ponadto odseparowanie aktywności projektowych od kolekcjonowania racjonalizacji umoż-


liwia programistom krytyczne i bardziej obiektywne przyglądanie się efektom swych prac.
Rekonstruowanie racjonalizacji opiera się jednak głównie na tych propozycjach, które wy-
brane zostały jako rozstrzygnięcia; propozycje odrzucone są szybko zapominane przez pro-
gramistów i ich utrwalanie jest znacznie trudniejsze.
Załóżmy na przykład, że nie prowadziliśmy kolekcjonowania racjonalizacji w związku
z tworzeniem systemu CTC i jedyna informacja związana z tym systemem zawarta jest w jego
projekcie, którego fragment dotyczący kontroli dostępu prezentuje się następująco.

4. Kontrola dostępu
Kontrola dostępu w systemie CTC realizowana jest na poziomie sekcji dyspozycyjnych (TrackSecti on):
dyspozytor (Dispatcher) przydzielony do danej sekcji może modyfikować ich stan, czyli prze-
stawiać sygnały i zwrotnice oraz operować innymi urządzeniami. Dyspozytor może również
oglądać stan sekcji przylegających do jego sekcji macierzystej, bez możliwości modyfikowania
ich stanu. Dyspozytor może w ten sposób obserwować pociągi zbliżające się do kontrolowanej
przez niego sekcji.

Kontrola dostępu zaimplementowana jest w postaci list uprawnień, utrzymywanych przez po-
szczególne obiekty TrackSection reprezentujące konkretne sekcje. Dla danej sekcji lista ta za-
wiera identyfikację dyspozytora uprawnionego do modyfikowania tej sekcji („pisarza") oraz
identyfikacje dyspozytorów uprawnionych do obserwowania ich stanu („czytelników") 6 . Ze
względów ogólności (rozszerzalności) lista zaimplementowana jest w ten sposób, że może za-
wierać wielu „czytelników" i wielu „pisarzy". Lista utrzymywana przez daną sekcję sprawdzana
jest przed wykonaniem każdej operacji odczytującej lub modyfikującej jej stan.

Gdy podsystem żąda subskrybowania określonej informacji, dotyczącej stanu danej sekcji, wy-
woływana przez niego usługa N o t i f i c a t i o n S e r v i c e kontaktuje się z tą sekcją w celu zweryfi-
kowania praw wspomnianego podsystemu do odczytywania jej stanu. Gdy modyfikuje się listę
uprawnień związaną z daną sekcją, sekcja ta wywołuje usługę N o t i f i c a t i o n S e r v i c e w celu
anulowania nieuprawnionych subskrypcji.

Powyższe rozwiązanie przedstawione jest na rysunku 12.14.

Rekonstruując racjonalizację na podstawie dokumentacji i przeglądów, zapisujemy każde


zagadnienie w formie tabeli z dwiema kolumnami, zawierającymi propozycje (lewa kolumna)
i argumenty „za" i „przeciw" tym propozycjom (prawa kolumna). W tabeli 12.5 widoczny jest
efekt takiej rekonstrukcji w odniesieniu do zintegrowania kontroli dostępu z mechanizmami
powiadamiania. Identyfikujemy dwa możliwe rozwiązania: P [ l ] , zgodnie z którym klasa
TrackSecti on eksportuje wszystkie operacje wymagające kontroli dostępu, oraz P[2], po-
legające na delegowaniu przez usługę Noti f i c a t i onServi ce kontroli dostępu do obiektu
TrackSection. Wyliczamy zalety i wady każdego z tych rozwiązań (w prawej kolumnie), a na
dole tabeli umieszczamy uzasadnienie decyzji o wyborze konkretnego rozwiązania.

6
To analogia do paradygmatu „czytelników i pisarzy", o tyle jednak daleka, że nie mamy tu do czynienia
z rywalizowaniem o dostęp do zasobów — przyp. tłum.
584 Rozdział 12. • Zarządzanie racjonalizacją

Tabela 12.5. Z r e k o n s t r u o w a n a racjonalizacja k o n t r o l i dostępu związanego z p o w i a d a m i a n i e m


w systemie CTC

I [1]: Jak powinno się zintegrować kontrolę dostępu z klasą TrackSection i usługą powiadamiania?

Kontrola dostępu w systemie CTC realizowana jest na poziomie sekcji dyspozycyjnych (TrackSecti on):
dyspozytor (Di spatcher) przydzielony do danej sekcji może modyfikować ich stan, czyli przestawiać
sygnały i zwrotnice oraz operować innymi urządzeniami. Dyspozytor może również oglądać stan
sekcji przylegających do jego sekcji macierzystej, bez możliwości modyfikowania ich stanu. Dyspozytor
może w ten sposób obserwować pociągi zbliżające się do kontrolowanej przez niego sekcji.

P[1]: Klasa TrackSection odpowiedzialna Na rzecz propozycji:


jest za modyfikowanie stanu sekcji oraz • Scentralizowane rozwiązanie — wszystkie
subskrypcje związane z powiadamianiem. c h r o n i o n e m e t o d y związane z klasą
Kontrola dostępu zaimplementowana jest w formie TrackSecti on zgrupowane są w tej klasie.
listy uprawnień utrzymywanej przez obiekty
klasy TrackSection. Uprawnienia sprawdzane
są dla każdej operacji odczytującej lub
modyfikującej stan wspomnianych obiektów.
Podsystem zamierzający subskrybować
p o w i a d o m i e n i a o zdarzeniach wywołuje
odpowiednie metody klasy TrackSection,
które z kolei przekazują wywołanie do usługi
powiadamiania (Noti f i c a t i onServi ce) pod
w a r u n k i e m pozytywnego zweryfikowania
u p r a w n i e ń do subskrypcji. Rozwiązanie
to przedstawione jest na rysunku 12.15.

P [2]: Klasa TrackSection odpowiedzialna jest Na rzecz propozycji:


wyłącznie za modyfikowanie stanu sekcji, usługa • Interfejs niezależny od kontroli dostępu:
powiadamiania (NotificationService) interfejsy klas Noti f i c a t i o n S e r v i ce
zarządza tylko powiadamianiem. i TrackSecti on są takie same jak w przypadku
P o d o b n i e jak P [1], z tą j e d n a k różnicą, nieobecności kontroli dostępu.
że podsystem żąda subskrypcji bezpośrednio • Przeciwko propozycji:
od usługi powiadamiania, która odwołuje się • Cykliczna zależność między klasami
do obiektu TrackSecti on w celu zweryfikowania Noti f i c a t i onServi ce i TrackSecti on: obiekt
uprawnień do tej subskrypcji. TrackSecti on wywołuje usługę powiadamiającą
w celu zasygnalizowania zdarzenia, zaś usługa
powiadamiająca odwołuje się do obiektu
T r a c k S e c t i o n w celu zweryfikowania
u p r a w n i e ń do subskrypcji.

R[l]:
P [ 2 ] . Propozycja P [1] mogłaby być lepszym rozwiązaniem, ze względu na wyłączenie usługi
powiadamiania z kontekstu kontroli dostępu, gdyby nie konieczność związanej z tym znaczącej
przeróbki istniejącego kodu. W przypadku propozycji P [2] przeróbka ta jest znacznie mniejsza.

Przedstawione rekonstruowanie racjonalizacji jest mniej kosztowne niż aktywności


uprzednio opisywane. Jest jednak znacznie trudniejsze pod względem dokumentowania od-
rzuconych propozycji i powodów ich odrzucenia, szczególnie gdy podejmowane decyzje były
wielokrotnie rewidowane. Rozstrzygnięciu z tabeli 12.5 towarzyszy wyjaśnienie, iż jest ono
12.5. Kierownicze aspekty zarządzania racjonalizacją 585

wyborem gorszej (nieoptymalnej) propozycji z określonych względów praktycznych (czyli


przeróbki kodu znacznie bardziej ograniczonej w porównaniu z wyborem propozycji opty-
malnej). O takich subtelnościach często jednak zapomina się z czasem.
Rekonstruowanie racjonalizacji może być efektywnym narzędziem przeglądu systemu,
szczególnie pod względem wyszukiwania decyzji niespójnych z celami projektowymi. I jeżeli
nawet zmiana tych decyzji okazuje się niewykonalna w późnych stadiach projektu, nabyta
przy tej okazji wiedza okaże się pożyteczna dla nowych programistów oraz programistów do-
konujących rewizji systemu w następnych iteracjach.
Wzajemne proporcje między kolekcjonowaniem, utrzymywaniem i rekonstruowaniem
racjonalizacji są różne dla odmiennych projektów i muszą zostać starannie określone przez
menedżera projektu. Spotyka się dość często kolekcjonowanie, przy znacznym wysiłku, po-
tężnej dawki informacji w większości bezużytecznej lub trudno dostępnej dla programistów,
którzy mogliby ją spożytkować. Zajmiemy się tym problemem w następnej sekcji.

12.5. Kierownicze aspekty zarządzania racjonalizacją


Gdy menedżer projektu żąda od programistów szczegółowego uzasadnienia podejmowanych
przez nich decyzji projektowych, często traktowany jest jak intruz, a same techniki związane
z racjonalizacją napotykają opór ze strony programistów i rychło przeradzają się w biurokrację.
Stanowi to duże wyzwanie dla menedżera, odpowiedzialnego za najważniejsze aspekty zarzą-
dzania racjonalizacją, między innymi:

• jej dokumentowanie (patrz sekcja 12.5.1),


• przypisywanie odpowiedzialności w zakresie kolekcjonowania i pielęgnowania modeli
racjonalizacji (patrz sekcja 12.5.2),
• zapewnienie komunikacji dotyczącej modeli racjonalizacji (patrz sekcja 12.5.3),
• negocjowanie zagadnień (patrz sekcja 12.5.4),
• rozwiązywanie konfliktów (patrz sekcja 12.5.5).

Jak poprzednio, koncentrujemy się na racjonalizacji związanej z etapem projektowania


systemu, opisywane techniki przydatne są jednak równie dobrze na wszystkich pozostałych
etapach.

12.5.1. Dokumentowanie racjonalizacji


Modele racjonalizacji (na przykład modele zagadnień) różnią się pod względem struktury od
modeli systemu (modelu przypadków użycia, modelu klas, kodu źródłowego). Jako że modele
typowego systemu są rozległe i skomplikowane, programiści wykorzystują rozmaite techniki
radzenia sobie z ich złożonością, organizując je w warstwy i hierarchie, wiążąc je z przypad-
kami użycia, czy też kojarząc dokumentację obiektów systemu z ich kodem źródłowym.
Modele racjonalizacji są z natury bardziej objętościowe od modeli systemu, obejmują bowiem
wszystkie propozycje związane z rozwiązywaniem poszczególnych problemów, argumenty
„za" i „przeciw" tym propozycjom, podjęte decyzje oraz ich uzasadnienia. Ponieważ podej-
mowane decyzje bywają rewidowane i zmieniane, modele racjonalizacji także nieustannie
ewoluują. Niewłaściwe jest zatem myślenie o racjonalizacji w kategoriach jedynie dokumentu,
586 Rozdział 12. • Zarządzanie racjonalizacją

gdyż każdy taki dokument rychło stawałby się nieaktualny i nieadekwatny do pozostałych do-
kumentów. Odpowiedniejszym podejściem jest utrzymywanie racjonalizacji w formie repozy-
torium, nieustannie aktualizowanego i uzupełnianego o nowe zagadnienia i decyzje. Zawartość
tego repozytorium musi być jednak utrzymywana w aktualnym stanie i łatwo dostępna dla
wszystkich zainteresowanych. Wymaganie to prowadzi wprost do ścisłego zintegrowania re-
pozytorium racjonalizacji z innymi narzędziami i procesami programistycznymi. Przykładem
narzędzia realizującego taką integrację jest REQuest; narzędzie to umożliwia formułowanie
przypadków użycia w kontekście ich racjonalizacji, o czym piszą A. H. Dutoit i B. Paech
[Dutoit i Paech, 2002]. Na rysunku 12.18 widzimy przykładowy ekran tej aplikacji; lewa
kolumna zapewnia dostęp do poszczególnych wymagań systemu, w podziale na przypadki
użycia i wymagania pozafunkcyjne, zaś w prawej kolumnie prezentowane są elementy racjo-
nalizacji związane z poszczególnymi wymaganiami, w kategoriach rozszerzonego modelu
QOC. Przyciski w górnej części lewej kolumny umożliwiają programistom formułowanie pytań
w różnych kategoriach (klarowności, kompletności, spójności, prawidłowości uformowania,
poprawności, uzasadnienia) i kojarzenie ich z aktualnie wyświetlanym elementem.
Zielone, żółte i czerwone wskaźniki towarzyszące elementom racjonalizacji wymienio-
nym w lewej kolumnie oznaczają status zagadnienia reprezentowanego przez ten element.
Klikając wspomniany wskaźnik, otrzymujemy opis odnośnego zagadnienia w prawej kolumnie.

Rysunek 12.18. REQuest — przykład repozytorium racjonalizacji zintegrowanego z narzędziem inżynierii


wymagań: elementy modelu racjonalizacji kojarzone są z poszczególnymi wymaganiami. W lewej kolumnie
wyświetlane są elementy modelu wymagań, prawa kolumna dedykowana jest modelowi zagadnień

Zintegrowanie elementów specyfikacji z widokami elementów racjonalizacji daje pro-


gramistom łatwy dostęp do aktualnej informacji; zapewnienie jej aktualności w dłuższej per-
spektywie, poprzez konsolidowanie i pielęgnowanie jej elementów, jest zadaniem wynikającym
z roli redaktora racjonalizacji; tę i inne role związane z zarządzaniem racjonalizacją opiszemy
w następnej sekcji.
12.5. Kierownicze aspekty zarządzania racjonalizacją 587

12.5.2. Przypisywanie odpowiedzialności


Właściwe przyporządkowanie odpowiedzialności związanej z kolekcjonowaniem i pielęgno-
waniem racjonalizacji jest najbardziej krytyczną decyzją menedżera, decydującą o rzeczywistej
użyteczności modeli racjonalizacji. Zarządzanie racjonalizacją sprowadzone do postaci biuro-
kratycznej procedury uzasadniania przez każdego programistę każdej podejmowanej przez
niego decyzji nie spełni swego zadania. Zamiast tego modele racjonalizacji powinny być utrzy-
mywane przez niewielką grupę osób — „historyków systemu" — na podstawie wszelkiej
informacji, jaka będzie dla nich dostępna ze strony programistów: szkiców dokumentów
projektowych, postów na grupach dyskusyjnych i tym podobnych. Ponieważ gromadzona
w ten sposób informacja jest jedną z najważniejszych, jakiej potrzebują programiści w zmaga-
niach się z konsekwencjami nieuchronnych zmian, w interesie samych programistów leży
troska o zapewnienie jej należytej szczegółowości i kompletności.
W związku ze wspomnianą na wstępie odpowiedzialnością za aktualność i użyteczność
informacji składającej się na racjonalizację, menedżer projektu wyznacza następujące role.

• Protokolant odpowiedzialny jest za kolekcjonowanie na bieżąco elementów racjo-


nalizacji pojawiających się w ramach zebrań; kolekcjonowanie to polega na spo-
rządzaniu chronologicznego zapisu dyskusji, który to zapis po zakończeniu zebrania
stanowi podstawę do sporządzenia strukturalnego protokołu dokumentuj ącego za-
gadnienia, propozycje, argumenty i rozstrzygnięcia. Pisaliśmy o tym w sekcji 12.4.2.
• Redaktor racjonalizacji odpowiedzialny jest za gromadzenie i organizację informa-
cji związanej z racjonalizacją. Informacja ta obejmuje między innymi strukturalne
protokoły z zebrań, prototypy, oceny poszczególnych technologii przez programi-
stów, szkice modeli systemu oraz dokumenty projektowe (lub ich szkice). Dla zebranej
informacji tworzony jest indeks według poszczególnych zagadnień. Redaktor racjo-
nalizacji wyręcza programistów w porządkowaniu i strukturalizowaniu informacji
składającej się na racjonalizację, ograniczając ich rolę do udostępniania tej informacji.
• Weryfikator racjonalizacji analizuje informację zebraną przez redaktora i iden-
tyfikuje napotkane w niej luki, korzystając z tych samych źródeł informacji, co re-
daktor. Nie jest to rola o charakterze menedżerskim, weryfikator powinien przekonać
programistów, iż uzyskanie od nich rzetelnych informacji opłaci się w dłuższej per-
spektywie. Role redaktora i weryfikatora często powierzane są tej samej osobie.

Rozmiar projektu determinuje liczbę protokolantów, redaktorów i weryfikatorów.


Przy przydzielaniu tych ról pomocne mogą okazać się następujące heurystyki.

• Jeden protokolant dla każdego zespołu. Zebrania są zwykle organizowane przez zespoły
zajmujące się poszczególnymi podsystemami lub zespół międzyfunkcyjny. Rolę proto-
kolanta mogą rotacyjnie pełnić poszczególni programiści, co przyczynia się do równo-
miernego rozłożenia tego czasochłonnego obowiązku w całym cyklu realizacji projektu.
• Jeden i ten sam redaktor racjonalizacji przez cały czas realizacji projektu. T a rola wy-
maga pełnoetatowego zaangażowania, w przeciwieństwie więc do rotacyjnej roli pro-
tokolanta wymaga zachowania spójności i z tego względu powinna być przypisana
pojedynczej osobie. W przypadku małych projektów rolę tę pełnić może architekt
systemu (patrz rozdział 6. „Projektowanie systemu — dekompozycja na podsystemy").
588 Rozdział 12. • Zarządzanie racjonalizacją

• Zwiększenie liczby weryfikatorów racjonalizacji po dostarczeniu systemu. Po dostar-


czeniu systemu klientowi zmniejsza się liczba programistów, którzy muszą być
bezpośrednio zaangażowani w projekt. Programistom odciążonym można powie-
rzyć role weryfikatorów, którzy powinni wydobyć tyle informacji związanej z ra-
cjonalizacją, ile to tylko możliwe — elementy tej informacji wciąż są jeszcze żywe
w umysłach programistów i należy je utrwalić, zanim naturalną koleją rzeczy odejdą
w zapomnienie.

12.5.3. Heurystyki komunikowania racjonalizacji


Duża część informacji komunikowanej w związku z projektem to informacja związana z ra-
cjonalizacją, każdy argument jest bowiem z definicji racjonalny, co wyjaśnialiśmy w sekcji
12.2. Programiści wymieniają argumenty dotyczące celów projektowych, istotności związanych
z nimi zagadnień, ich ewaluacji i tak dalej. Na racjonalizację systemu składa się ogrom skom-
plikowanej informacji, znacznie obszerniejszej niż sam system. Informacja ta wymieniana jest
najczęściej w kręgu małych forów, między innymi na zebraniach zespołu i w ramach niefor-
malnych konwersacji przy automacie z kawą. Podstawowym wyzwaniem pod adresem za-
rządzania racjonalizacją jest udostępnienie tej informacji zainteresowanych uczestnikom,
z zachowaniem treści, lecz bez przeładowywania formy. Opisaliśmy już kilka technik ko-
lekcjonowania i strukturalizowania racjonalizacji, między innymi przez modelowanie zagad-
nień w protokołach i reprezentowanie racjonalizacji w formie repozytoriów. Uzupełnieniem
tych technik mogą być poniższe heurystyki, które sprawiają, że struktura racjonalizacji staje
się bardziej czytelna i łatwiej po niej nawigować.

• Wybieraj nazwy zagadnień w sposób spójny. Zagadnienia p o w i n n y być opatrywane


spójnymi nazwami, unikalnymi w obrębie protokołów, grup dyskusyjnych, komu-
nikatów e-mail i dokumentów. Dla ułatwienia identyfikacji w nazwie każdego zagad-
nienia powinien być obecny jego numer i krótka, sugestywna fraza określająca jego
kategorię (na przykład „zagadnienie_dostęp/powiadamianie").
• Scentralizuj zagadnienia. Mimo iż zagadnienia mogą być w dyskutowane w różnych
kontekstach, jeden z nich (grupę dyskusyjną, bazę zagadnień) należy specjalnie wy-
różnić jako ich centralne repozytorium. Baza zagadnień powinna być utrzymywana
przez redaktora racjonalizacji, lecz każdy programista powinien mieć możliwość jej
odczytywania i rozszerzania o nowe pozycje. Ułatwi to programistom szybkie wy-
szukiwanie niezbędnych informacji.
• Zapewnij powiązania zagadnień z odnośnymi elementami systemu. W i ę k s z o ś ć za-
gadnień odnosi się do konkretnych elementów modeli systemu (przypadków użycia,
obiektów, podsystemów). Identyfikowanie zagadnień związanych z konkretnym ele-
mentem systemu jest sprawą oczywistą, relacja odwrotna — identyfikowanie elementu
systemu, do którego odnosi się konkretne zagadnienie — jest zdecydowanie bardziej
problematyczna. By relację ta stała się bardziej użyteczna i czytelna, należy wiązać
każde zagadnienie w momencie jego powstawania z elementem systemu, którego
dotyczy.
12.5. Kierownicze aspekty zarządzania racjonalizacją 589

• Zarządzając zmianami, pamiętaj o racjonalizacji. Racjonalizacja s y s t e m u e w o l u u j e


wraz z jego modelami, zatem powinna podlegać zarządzaniu konfiguracją w takim
samym stopniu jak wspomniane modele.

Kolekcjonowanie i strukturalizowanie racjonalizacji nie tylko usprawnia jej komuniko-


wanie, lecz także ułatwia komunikację związaną z modelami systemu. Zintegrowanie obu
tych kategorii informacji ułatwia programistom utrzymywanie spójności między nimi.

12.5.4. Modelowanie i negocjowanie zagadnień


Najważniejsze decyzje związane z projektem są w większości przypadków wynikiem negocjacji
— różne strony, reprezentujące różne, często sprzeczne interesy wypracowują konsensus
w sprawie wybranego aspektu systemu. Analiza wymagań wiąże się z negocjowaniem kształtu
funkcjonalnego systemu z klientem, projektowanie systemu wymaga negocjowania interfejsów
między programistami, integrowanie podsystemów wymaga rozwiązywania konfliktów między
programistami. Modele zagadnień są wygodną formą reprezentowania informacji wymie-
nianej w czasie negocjowania, mogą być jednak równie użyteczne w dziele ułatwiania samych
negocjacji.
Tradycyjne negocjowanie, polegające na trwaniu z uporem na swych pozycjach, jest czę-
sto czasochłonne i bezowocne, szczególnie gdy stanowiska negocjujących stron są wyraźnie
sprzeczne. Cały wysiłek kierowany jest wówczas na akcentowanie zalet własnego stanowiska,
przy jednoczesnym wytykaniu wad stanowiska prezentowanego przez drugą stronę. Jeżeli na-
wet obie strony zainteresowane są osiągnięciem porozumienia, perspektywa zmiany prezen-
towanego nastawienia postrzegana bywa jako ryzyko utraty wiarygodności. Takie negocjacje
z trudem (jeżeli w ogóle) posuwają się do przodu, prowadząc do wypracowania jakiegoś kom-
promisu.
Jedną ze znanych metodologii pokonywania opisanych trudności jest tak zwana har-
wardzka metoda negocjowania, opisana w książce R. Fishera, W. Uryego i B. Pattona [Fisher
i in., 1991], koncentrująca przebieg negocjacji na ich przedmiocie, a nie na negocjujących
stronach. W przełożeniu na modelowanie zagadnień podstawowe założenia tej metody można
sformułować następująco.

• Oddzielaj propozycje od formułujących je programistów. Programiści wkładają tak


wiele wysiłku w wypracowywanie konkretnych propozycji i w efekcie tak silnie się
z nimi identyfikują, że krytykowanie danej propozycji odbierane jest jak osobisty
atak na jej autora. Wyraźne oddzielenie propozycji od osoby jej autora ułatwia dys-
kutowanie i być może odrzucenie. Można ten cel osiągnąć, wypracowując poszcze-
gólne propozycje kolektywnie (nie jednoosobowo) lub angażując obie negocjujące
strony w ich opracowywanie. Programiści łatwiej rezygnują ze swych propozycji,
jeśli te nie wiążą się jeszcze z zaangażowaniem znacznych zasobów, stąd oczywisty
wniosek, by negocjowanie propozycji odbywało się jeszcze przed rozpoczęciem im-
plementowania ich następstw.
• Koncentruj się na kryteriach, nie propozycjach. Programiści bronią swych propozycji
(i podważają propozycje drugiej strony), działając zgodnie z pewnymi kryteriami
— wysuwane przez nich propozycje są zapewne zgodne z ich własnymi kryteriami,
590 Rozdział 12. • Zarządzanie racjonalizacją

lecz niekoniecznie z kryteriami przyjmowanymi przez innych programistów. Kon-


flikt w negocjacjach jest więc najczęściej tak naprawdę konfliktem między kryteriami
— gdy kryteria staną się w pełni jawne, łatwiejsze będzie wypracowanie kompromisu.
Gdy uzgodniony zostanie zestaw kryteriów satysfakcjonujący wszystkie negocjujące
strony, negocjowanie poszczególnych propozycji nabierze bardziej obiektywnego
charakteru i stanie się daleko mniej kontrowersyjne.
• Uwzględniaj wszystkie kryteria, zamiast faworyzować jedno wybrane. R ó ż n i c a kryte-
riów przyjętych przez poszczególne strony negocjacji wynika wprost z różnicy intere-
sów tych stron: kryteria wydajnościowe motywowane są przez względy użyteczności
systemu, kryteria modyfikowalności — przez względy jego pielęgnacji i tak dalej. Nawet
jeśli pewne kryteria wyraźnie górują nad innymi pod względem priorytetu, zupełne
ignorowanie tych o niższym priorytecie może równać się ignorowaniu interesów jed-
nej lub kilku negocjujących stron.

Postrzeganie realizacji projektu w kategoriach negocjacji uwydatnia jego aspekt spo-


łeczny. Programiści są przecież ludźmi, a zatem — poza oczywistym nastawieniem technicz-
nym — prezentują określony stosunek emocjonalny do poszczególnych ewentualności roz-
wiązania określonego problemu. Przekłada się to w prostej linii na interpersonalne relacje
między programistami i stanowić może genezę rozmaitych konfliktów natury osobistej. Spro-
wadzenie zagadnienia do obiektywnego, czysto technicznego wymiaru — co osiągnąć można
właśnie dzięki ich modelowaniu — zdecydowanie sprzyja łagodzeniu opisanego zjawiska.
Przykładem tak ukierunkowanego modelu jest WinWin — narzędzie wspomagające
zbieranie wymagań i ułatwiające negocjowanie stron prezentujących odmienne punkty
widzenia, które w swej pracy opisali B. Boehm, A. Egyed, J. Kwan, D. Port, A. Shah i R. Ma-
dachy [Boehm i in., 1998]. U podstaw jego powstania legła znana prawda, iż warunkiem ko-
niecznym pomyślności w realizacji projektu jest zadowolenie jego głównych uczestników.
Często bowiem postęp w negocjacjach uwarunkowany jest nie tyle rozstrzyganiem kom-
promisów między negocjującymi stronami, ile właśnie rozpoznaniem kryteriów przyjmowa-
nych przez negocjujące strony. Jawność wszystkich wspomnianych kryteriów ułatwia rozpo-
znawanie konfliktów między nimi i poszukiwanie kompromisowych rozwiązań.
WinWin wykorzystuje model podobny do QOC (patrz rysunek 12.19), od którego różni
się uporządkowaniem węzłów w hierarchie taksonomii. Oryginalne węzły kryteriów modelu
QOC tutaj nazywane są „warunkami zwycięstwa" (Wi n Condi tion) i reprezentują kryteria
sukcesu poszczególnych stron negocjacji. Zgodnie z modelem, proces rozpoczyna się od zi-
dentyfikowania głównych „graczy" i ich „warunków zwycięstwa"; skonfliktowane ze sobą
„warunki zwycięstwa" modelowane są w postaci zagadnień. Gdy osiągnięte zostanie poro-
zumienie (Agreement), jest ono wprowadzane do bazy WinWin i kojarzone ze wspomnianymi
warunkami. Ułatwia to wypracowywanie i dokumentowanie osiągniętych kompromisów.

12.5.5. Strategie rozwiązywania konfliktów


Niestety, czasami zdarza się tak, że wypracowanie kompromisu w negocjacjach okazuje się
nieosiągalne. W takiej sytuacji trzeba sięgnąć po określone strategie rozwiązywania konflik-
tów. Najgorszymi bowiem decyzjami projektowymi są te, które nie zostały podjęte wskutek
12.5. Kierownicze aspekty zarządzania racjonalizacją 591

Rysunek 12.19. Model zagadnień systemu Win Win

braku porozumienia i braku wspomnianych strategii. Odkładanie istotnych decyzji na póź-


niejsze fazy projektu może wiązać się z kolosalnymi nawet kosztami zmian w projekcie i ko-
lekcjonowania kryjącej się za nimi racjonalizacji.
Spośród wielu istniejących strategii rozwiązywania konfliktów ograniczymy się do pię-
ciu poniższych.

• Większość ma rację. Głosowanie na zasadzie „decyduje większość" może przełamać


impas i przyczynić się do podjęcia decyzji. Rozmaite narzędzia wspomagające współ-
pracę grupową umożliwiają przypisanie wag istotności poszczególnym argumentom
w modelu zagadnień i wyłonienie „zwycięskiej" propozycji na podstawie formuły
arytmetycznej, o czy można przeczytać w pracy M.Purvisa, M. Purvis i P. Jonesa
[Purvis i in., 1996]. Zakłada się przy tym równouprawnienie wszystkich dyskutan-
tów i statystyczne oczekiwanie, że grupa zwykle podejmuje właściwe decyzje.
• Właściciel ma ostatnie słowo. Zgodnie z tą strategią rozstrzygający głos należy do
„właściciela" zagadnienia, czyli osoby, która je sformułowała.
• Menedżer ma zawsze rację. Ta strategia jest szczególnie przydatna w warunkach
hierarchicznej organizacji projektu: gdy grupa nie potrafi wypracować konsensusu,
jej menedżer narzuca swą decyzję, wypracowaną na podstawie argumentów pre-
zentowanych przez członków tej grupy. Zakłada się tu, że menedżer jest w stanie
należycie zrozumieć wspomniane argumenty.
• Ekspert ma zawsze rację. W ramach tej strategii rozstrzygnięcia dokonuje niezależny
ekspert, niezaangażowany w debatę, na podstawie oceny sytuacji i swego doświad-
czenia. Przykładowo na etapie analizy wymagań ekspertem takim może być testujący
użytkownik, któremu prezentuje się poszczególne propozycje rozwiązania zagad-
nienia. Wadą tej strategii jest fakt, że ów ekspert może mieć nikłą wiedzę na temat
innych zagadnień i generalnie na temat kontekstu dyskutowanego zagadnienia.
• Niech czas zdecyduje. Gdy dane zagadnienie pozostaje nierozwiązane, upływający
czas wywiera coraz większą presję na znalezienie rozstrzygnięcia i podjęcie decyzji.
Wraz z upływem czasu mogą pojawić się nowe okoliczności — zdefiniowanie nowych
592 Rozdział 12. • Zarządzanie racjonalizacją

aspektów systemu czy podjęcie ważnych decyzji — które znacznie ułatwią rozwiąza-
nie patowego dotąd problemu. Niebezpieczeństwem tej strategii jest zagrożenie, iż
wymusza ona podejmowanie decyzji krótkowzrocznych, optymalizujących kryteria
krótkoterminowe — na przykład łatwość implementacji — a ignoruje cele daleko-
siężne, takie jak modyfikowalność i pielęgnowalność.

Strategie „Większość ma rację" i „Właściciel ma ostatnie słowo" są najgorszymi z moż-


liwych, bowiem oznaczają ignorowanie racji sporej (być może) liczby uczestników. Strategie
„Menedżer ma zawsze rację" i „Ekspert ma zawsze rację" prowadzą do lepszego rezultatu, pod
warunkiem wszakże, iż menedżer (ekspert) ma wystarczającą wiedzę na temat problemu bę-
dącego przedmiotem sporu. Strategia „Niech czas zdecyduje" oznacza tymczasową ucieczkę
od problemu, na dłuższą metę niosącą ryzyko kosztownych przeróbek.
W praktyce najpierw należy dążyć do osiągnięcia konsensusu. Gdy dążenia okazują się
bezowocne, należy pozostawić decyzję menedżerowi lub ekspertowi. Gdy ci nie potrafią do-
konać właściwego wyboru, pozostaje głosowanie jako ostateczność.

12.6. Literatura uzupełniająca


Większość prac związanych z modelowaniem zagadnień ma swe źródło w publikacji W. Kunza
i H. Rittela [Kunz i Rittel, 1970], choć opisuje ona wykorzystywanie modelu IBIS w kontekście
negocjowania złożonych problemów politycznych. Zastosowanie modelu IBIS na gruncie
inżynierii oprogramowania nastąpiło w późnych latach 80. ubiegłego stulecia, w postaci
narzędzia gIBIS, omówionego w pracy J. Conklina i K. C. Burgessa-Yakemovica [Conklin
i Burgess-Yakemovic, 1991], a wykorzystywanego z powodzeniem w przemyśle do wielu
analiz przypadków. Narzędzie gIBIS stało się też punktem wyjścia dla innego (komercyjnego)
narzędzia QuestMap, wykorzystywanego do kolekcjonowania i strukturalizowania racjonali-
zacji w czasie zebrań.
Na początku lat 90. ubiegłego wieku alternatywą IBIS dla stał się QOC, koncentrujący
się na systematycznym wartościowaniu propozycji według przyjętych kryteriów (zamiast
okazjonalnie formułowanych argumentów), o czy piszą A. MacLean, R. M. Young, V. Bellotti
i T. Moran [MacLean i in., 1991]. Oba modele podzieliły odtąd rynek i IBIS wykorzystywany
jest w kontekście racjonalizacji gromadzonej „w locie", podczas gdy QOC jest bardziej przy-
datny do długofalowego reprezentowania informacji związanej z racjonalizacją.
W literaturze przedmiotu spotyka się niewiele przykładów zastosowania racjonalizacji
w tworzeniu oprogramowania — do nielicznych należą książki T. P. Morana i J. M. Carrolla
[Moran i Carroll, 1996] oraz A. H. Dutoita, R. McCalla, I. Mistrika i B. Peacha [Dutoit i in.,
2006]. Efektywne wykorzystywanie racjonalizacji w procesie tworzenia oprogramowania jest
trudnym zadaniem ze względów zarówno technicznych (modele racjonalizacji są obszerne,
trudne w zarządzaniu i wykorzystywaniu), jak i pozatechnicznych — kolekcjonowanie racjo-
nalizacji wiąże się z wysiłkiem, którego beneficjantami stają się głównie inne osoby, o czym
piszą A. H. Dutoit i B. Paech [Dutoit i Paech, 2001]. Istnieją jednak liczne fakty, ilustrujące
wyjątkowe znaczenie gromadzenia i integrowania racjonalizacji dla powodzenia rozmaitych
przedsięwzięć. W tym rozdziale ograniczyliśmy się do trzech narzędzi wspierających stosowa-
nie racjonalizacji w inżynierii oprogramowania: WinWin [Boehm i in., 1998], NFR Frame-
work [Chung i in., 1999] i REQuest [Dutoit i Paech, 2002].
11.7. Ćwiczenia 593

12.7. Ćwiczenia
12.1. W sekcji 12.3 o m a w i a l i ś m y z a g a d n i e n i e z w i ą z a n e z k o n t r o l ą d o s t ę p u i p o w i a d a -
m i a n i e m w systemie C T C . P o d a j p r z y k ł a d i n n e g o zagadnienia, jakie m o ż e w y n i k n ą ć
w związku z t w o r z e n i e m takiego systemu, przedstaw możliwe propozycje, kryteria
i a r g u m e n t y . Z a p r o p o n u j oraz u z a s a d n i j rozstrzygnięcie. O t o d w a m o ż l i w e p r z y k ł a d y
wspomnianych zagadnień:

• Jak z a p e w n i ć s p ó j n o ś ć m i ę d z y s e r w e r e m g ł ó w n y m (mai n S e r v e r ) a z a p a s o w y m
(hotBackup)?

• Jak w y k r y w a n e b ę d ą a w a r i e s e r w e r a g ł ó w n e g o i j a k z a i m p l e m e n t o w a n e b ę d z i e
przejęcie pracy przez serwer zapasowy?

12.2. W y o b r a ź sobie, że t w o r z y s z p r o g r a m n a r z ę d z i o w y w s p o m a g a j ą c y m o d e l o w a n i e
w języku U M L i p o s t a n o w i ł e ś ściśle z i n t e g r o w a ć z a r z ą d z a n i e racjonalizacją z i n n y m i
m e c h a n i z m a m i modelowania. Opisz, w jaki sposób programista wykorzystujący
T w o j e narzędzie kojarzył będzie z a g a d n i e n i a z i n n y m i e l e m e n t a m i m o d e l u . N a r y s u j
d i a g r a m klas p r z e d s t a w i a j ą c y m o d e l z a g a d n i e ń i w s p o m n i a n e s k o j a r z e n i a .

12.3. Poniższy f r a g m e n t jest w y j ą t k i e m d o k u m e n t u S D D dla s y s t e m u FRIEND o p i s u j ą c y m


w języku n a t u r a l n y m racjonalizację decyzji w y b o r u relacyjnej b a z y d a n y c h j a k o m a -
gazynu przechowywania obiektów trwałych. Narysuj m o d e l zagadnień obejmujący
zagadnienia, p r o p o z y c j e , a r g u m e n t y i kryteria z a w a r t e w t y m opisie ( p o d o b n i e j a k
u c z y n i l i ś m y t o w sekcji 12.3).

Jednym z fundamentalnych problemów przy projektowaniu podsystemu przechowywania danych


jest wybór silnika bazodanowego. Początkowo jedno z wymagań pozafunkcyjnych nakazywało
użycie w tej roli obiektowej bazy danych, jako alternatywę wymieniono natomiast relacyjną bazę
danych, „płaskie" pliki oraz kombinację tych dwóch mechanizmów. Obiektowa baza danych daje
wymierne korzyści w postaci automatycznego zarządzania skomplikowanymi powiązaniami
między przechowywanymi obiektami, jest jednak mało wydajna w stosunku do dużych rozmia-
rów danych i częstych operacji wyszukiwania, poza tym dostępne obecnie produkty tej kategorii
nie integrują się dobrze z mechanizmem CORBA, ze względu na brak wsparcia cech specyficz-
nych dla niektórych języków programowania, między innymi skojarzeń w języku Java. Relacyjne
bazy danych oferują rozwiązanie bardziej niezawodne, bardziej wydajne, a także większy wybór
produktów i większe wsparcie w postaci licznych narzędzi. Ponadto relacyjne bazy danych integrują
się bezproblemowo z mechanizmem CORBA. Relacyjne bazy danych nie zapewniają jednak
bezpośredniego wsparcia dla implementacji skomplikowanych powiązań między danymi. Za-
proponowano także specjalne potraktowanie danych tworzonych jednorazowo i odczytywanych
bardzo rzadko, lecz koniecznych do przechowywania przez dłuższy czas — na przykład odczy-
tów wskazań czujników, nagrywanych rozmów czy migawek z kamer: ze względu na skąpe po-
wiązania tych danych z innymi elementami najbardziej odpowiednim mechanizmem ich prze-
chowywania byłyby niezależne pliki, zdolne przechowywać niejednorodne dane dużych
rozmiarów i łatwo poddające się archiwizowaniu. System niezależnych plików wymagałby jed-
nak napisania „od zera" wszelkiego kodu związanego z zarządzaniem ich zawartością, w tym
kodu odpowiedzialnego za serializację danych i szeregowanie dostępu. Ostatecznie wybraliśmy
relacyjną bazę danych, bazując na wymaganiach związanych z mechanizmem CORBA i stosun-
kowo prostym charakterem powiązań między elementami przechowywanych danych.
594 Rozdział 12. • Zarządzanie racjonalizacją

12.4. N a r y s u j m o d e l Q O C r ó w n o w a ż n y g r a f o w i celów f r a m e w o r k u N F R z r y s u n k u 12.12.


P r z e d y s k u t u j w a d y i zalety z a s t o s o w a n i a Q O C i N F R dla r e p r e z e n t o w a n i a racjonali-
zacji w p r o c e s i e z b i e r a n i a i a n a l i z o w a n i a w y m a g a ń .

12.5. W y o b r a ź sobie, że integrujesz system r a p o r t o w a n i a b ł ę d ó w z n a r z ę d z i e m z a r z ą d z a n i a


konfiguracją w celu śledzenia w y k r y w a n y c h błędów, ich poprawiania, żądania n o w y c h
c e c h i r o z s z e r z e ń . I n t e g r a c j ę tę c h c e s z z r e a l i z o w a ć za p o m o c ą m o d e l u z a g a d n i e ń .
N a r y s u j d i a g r a m klas tego m o d e l u , uwzględniający elementy zarządzania konfiguracją,
elementy raportowania błędów i dyskusje związane z tymi elementami.

Bibliografia
[Boehm i in., 1998] B. Boehm, A. Egyed, J. Kwan, D. Port, A. Shah, R. Madachy
Using the WinWin spiral model: A case study, „IEEE Computer"
31(7): str. 3 3 - 4 4 , 1998.
[Chung i in., 1999] L. Chung, B. A. Nixon, E. Yu, J. Mylopoulos Non-Functional
Requirements in Software Engineering, Kluwer Academic,
Boston, 1999.
[Conklin i Burgess-Yakemovic, 1991] J. Conklin, K. C. Burgess-Yakemovic A process-oriented
approach to design rationale, „Human-Computer Interaction",
t.6, str. 3 5 7 - 391, 1991.
[Dutoit i Paech, 2001] A.H. Dutoit, B. Paech „Rationale management in software
engineering" w: S. K. Chang (red.) Handbook of Software
Engineering and Knowledge Engineering, t.l, World
Scientific Publishing, 2001.
[Dutoit i Paech, 2002] A. H. Dutoit, B. Paech Rationale-based use case
specification, „Requirements Engineering Journal", 7(1):
str. 3 - 19, 2002.
[Dutoit i in., 2006] A. H. Dutoit, R. McCall, I. Mistrik, B. Paech (red.) Rationale
Management in Software Engineering, Springer,
Heidelberg, 2006.
[Fisher i in., 1991] R. Fisher, W . Ury, B. P a t t o n Dochodząc do TAK.
Negocjowanie bez poddawania się, Polskie Wydawnictwo
Ekonomiczne, Warszawa, 2000.
[Kunz i Rittel, 1970] W . Kunz, H. Rittel „Issues as elements of i n f o r m a t i o n
systems", Working Paper Nr 131, Institut fur Grundlagen
der Plannung, Universitat Stuttgart, Germany, 1970.
[Lee, 1990] J. Lee „A qualitative decision management system" w: P. H.
Winston, S. Shellard (red.) Artificial Intelligence at MIT:
Expanding Frontiers, t.l, str. 104 - 133, MIT Press, Cambridge,
MA, 1990.
[MacLean i in., 1991] A. MacLean, R. M. Young, V. Bellotti, T. Moran „Questions,
options, and criteria: Elements of design space analysis",
„ H u m a n - C o m p u t e r Interaction", t.6, str. 201 - 250,1991.
[Moran i Carroll, 1996] T. P. Moran, J. M. Carroll (red.) Design Rationale: Concepts,
Techniques, and Use, Lawrence E r l b a u m Associates,
Mahwah, NJ, 1996.
Bibliografia 595

[Purvis i in., 1996] M. Purvis, M. Purvis, P. Jones „A group collaboration tool


for software engineering projects", Conference proceedings
of Software Engineering: Education and Practice (SEE P'96),
Dunedin, NZ, styczeń 1996.
13.1. Wstęp: samoloty 598

13.2. O zarządzaniu konfiguracją ogólnie 600

13.3. Koncepcje zarządzania konfiguracją 602


13.3.1. Elementy konfiguracji i agregaty CM 603
13.3.2. Wersje i konfiguracje 604
13.3.3. Żądania zmian 604
13.3.4. Promocje i emisje 605
13.3.5. Repozytoria i przestrzenie robocze 606
13.3.6. Schematy identyfikowania wersji 606
13.3.7. Zmiany i zbiory zmian 608
13.3.8. Narzędzia wspomagające zarządzanie konfiguracją 610
13.4. Aktywności tworzące zarządzanie konfiguracją 611
13.4.1. Identyfikowanie elementów konfiguracji i agregatów CM 613
13.4.2. Zarządzanie promocjami 615
13.4.3. Zarządzanie emisjami 616
13.4.4. Zarządzanie gałęziami 619
13.4.5. Zarządzanie wariantami 623
13.4.6. Zarządzanie propozycjami zmian
i ich implementowaniem 626

13.5. Kierownicze aspekty zarządzania konfiguracją 627


13.5.1. Dokumentowanie zarządzania konfiguracją 627
13.5.2. Przypisywanie odpowiedzialności 628
13.5.3. Planowanie aktywności w ramach zarządzania konfiguracją 629
13.5.4. Integracja ciągła jako optymalizacja zarządzania promocjami
i ich testowaniem 630

13.6. Literatura uzupełniająca 632

13.7. Ćwiczenia 633

Bibliografia 634
Zarządzanie konfiguracją

Ci, którzy chcieliby powtarzać przeszłość,


powinni zajmować się nauczaniem historii.
— Frank Herbert: Diuna: Kapitularz

^Proces tworzenia systemu naznaczony jest organicznie piętnem zmian: zmieniająsię


wymagania, gdy programiści zyskują coraz lepsze rozumienie dziedziny aplikacyjnej; zmienia
się projekt systemu w obliczu nowych celów projektowych nowych technologii; zmienia się
projekt obiektów w miarę identyfikowania nowych obiektów dziedziny realizacyjnej; zmienia
się wreszcie implementacja, gdy usuwane są przyczyny pojawiających się błędów. Zmiany
dotyczą każdego produktu, od modeli systemu do kodu źródłowego i dokumentacji. Proces
monitorowania i rejestrowania tych zmian nosi nazwę zarządzania konfiguracją: od mo-
mentu, gdy dany produkt uznany zostanie za „linię bazową" (baseline), czyli formalną postać
początkową, wszelkie zmiany, jakim jest poddawany, są szczegółowo dokumentowane i wyma-
gają formalnego zatwierdzenia. Zmniejsza to znacznie ryzyko wprowadzania do tworzonego
systemu niespójności i niejednoznaczności.
W tym rozdziale zajmiemy się następującymi aktywnościami związanymi z zarządza-
niem konfiguracją:

• identyfikowaniem elementów konfiguracji, czyli modelowaniem systemu w katego-


riach zbioru ewoluujących komponentów;
• zarządzaniem promocjami, czyli kontrolowaniem wersji oprogramowania prze-
znaczonych dla programistów;
• zarządzaniem emisjami, czyli kontrolowaniem wersji oprogramowania prze-
znaczonych dla klienta i użytkowników;
• zarządzaniem dywergencjami, czyli równolegle rozwijanymi odmianami tego sa-
mego produktu;
• zarządzaniem wariantami, czyli współistniejącymi różnymi wersjami oprogra-
mowania;
• zarządzaniem zmianami, czyli przetwarzaniem żądań zmian, ich aprobowaniem
i śledzeniem ich realizacji.

Rozdział zakończymy opisem zarządzania zmianami z perspektywy menedżera projektu.


Omówimy między innymi procesy integracji ciągłej, automatyzujące proces tworzenia pro-
mocji i wykonywanie związanych z nimi testów regresyjnych, co umożliwia wczesne odkrycie
problemów wynikających z integrowania komponentów.
598 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

13.1. Wstęp: samoloty


S a m o l o t y p a s a ż e r s k i e n a l e ż ą d o n a j b a r d z i e j s k o m p l i k o w a n y c h dzieł i n ż y n i e r s k i c h : m u s z ą
być bezpieczne, niezawodne i jednocześnie ekonomiczne. Samolotu, w przeciwieństwie do
s a m o c h o d u czy p o c i ą g u , n i e m o ż n a tak p o p r o s t u z a t r z y m a ć w p r z y p a d k u awarii; k o m p a n i e
lotnicze, w przeciwieństwie d o N A S A , m a j ą z o b o w i ą z a n i a w o b e c swych akcjonariuszy, k t ó r y c h
m u s z ą n a bieżąco i n f o r m o w a ć o zyskach i stratach, i k t ó r y m m u s z ą płacić n a l e ż n e d y w i d e n d y .
E f e k t e m t y c h u w a r u n k o w a ń jest b a r d z o s k o m p l i k o w a n y p r o j e k t o w y s o k i e j r e d u n d a n c j i ,
o b e j m u j ą c y d u ż ą liczbę s y s t e m ó w diagnostycznych, n a p r z y k ł a d Boeing 747 składa się z p o n a d
sześciu m i l i o n ó w części. A b y t w o r z y ć i u t r z y m y w a ć t a k s k o m p l i k o w a n e m a s z y n e r i e , a j e d -
nocześnie rywalizować z konkurencją, producenci samolotów w jak największym stopniu
w y k o r z y s t u j ą r a z o p r a c o w a n e rozwiązania, p r z e d ł u ż a j ą c życie d a n e g o m o d e l u p r z e z p r z y r o -
stowe w p r o w a d z a n i e d o niego kolejnych u s p r a w n i e ń . Przykładowo Boeing 747 zaistniał w r o k u
1967 r o k u i o d p o n a d c z t e r d z i e s t u lat w c i ą ż jest p r o d u k o w a n y . Jego p r o j e k t , p o c z ą w s z y o d
p i e r w s z e j wersji, p o d d a n y został c z t e r e m z n a c z ą c y m p r z e o b r a ż e n i o m — e f e k t e m o s t a t n i e g o ,
z r o k u 1988, jest m o d e l 747-400. I n n ą m e t o d ą r a d z e n i a sobie z o p i s a n ą złożonością jest w y k o -
rzystywanie j a k w n a j w i ę k s z y m s t o p n i u poszczególnych e l e m e n t ó w i p o d s y s t e m ó w istniejących
modeli. Rozważmy poniższy przykład.

Airbus A320

W roku 1988 Airbus Industries — konsorcjum europejskich producentów samolotów — udostęp-


niło A320, pierwszy samolot pasażerski typu fly-by-wire[ — podobnie jak w samolocie FI6, pilot ma
do dyspozycji manetkę (joystick), której ruchy interpretowane są przez komputer kontrolujący uło-
żenie skrzydeł i powierzchni sterowych. W przeciwieństwie do bezpośredniego — mechanicznego
lub hydraulicznego — sprzężenia manetek z elementami wykonawczymi, wspomniany komputer
weryfikuje działania pilota, uniemożliwiając przekraczanie założonych parametrów granicznych,
takich jak minimalna i maksymalna prędkość, maksymalny kąt natarcia czy maksymalne przeciążenie.
A320 w pełnym obsadzeniu przewoził 150 pasażerów na krótkich i średniej długości trasach.

A321 i A 3 1 9

Sukces A320 skłonił konsorcjum Airbus do opracowania dwóch zbliżonych modeli: krótszego,
124-miejscowego A319 i dłuższego, 185-miejscowego A321. Projektowanie nowego modelu po-
przez skrócenie albo wydłużenie istniejącego to powszechna praktyka w przemyśle lotniczym. Pro-
ducentom pozwala na zmniejszenie kosztów i skrócenie czasu produkcji poprzez wykorzystywanie
dużej części istniejącego projektu, liniom lotniczym umożliwia zmniejszenie kosztów operacyjnych,
między innymi przez używanie tego samego zestawu części zamiennych. Airbus posunął się nieco
dalej: wszystkie trzy modele mają taki sam pulpit sterowniczy i tę samą charakterystykę sterowania
— A321 wyposażono w klapy ze szczelinami i nieco zmodyfikowane skrzydła, by sterowało się nim
podobnie jak A320, od którego jest dłuższy i cięższy. W konsekwencji pilot posiadający certyfikat na
dowolny z wymienionych modeli może pilotować pozostałe dwa, co powoduje kolejne oszczędności
w postaci mniejszej liczby szkoleń pilotów i personelu serwisującego, jednego typu symulatora lotów
i wykorzystywaniu takich samych części zamiennych do wszystkich trzech modeli.

1
Dosł. „latanie za pomocą kabli" — elektroniczny system sterowania samolotem, eliminujący mecha-
niczne połączenia z elementami steru. Patrz także http://plwikipedia.org/wiki/Fly-by-wire —przyp. tłum.
13.1. Wstęp: samoloty 599

A330 i A340
Dwa kolejne modele — A330 i A340 — to dalsze kroki na drodze rozwojowej airbusów. Zaprojek-
towane na średnie i dalekie trasy mogą zabierać dwukrotnie więcej pasażerów niż A320 i latać trzy
razy dalej. Mają taki sam kokpit i pulpit sterowniczy jak A320; pilot posiadający certyfikat na modele
rodziny A320 może prowadzić modele A330 i A340 po niewielkim przeszkoleniu. Dla odróżnienia
— Boeing 737 (który można porównać do A319) ma charakterystykę sterowania całkowicie różną
od Boeinga 747 (który można by porównać do A340).

Przyrostowe doskonalenie podsystemów i ich wielokrotne wykorzystywanie nie obywa


się bez problemów. Każda zmiana wprowadzona do modelu A320 musi być starannie prze-
analizowana w kontekście pozostałych dwóch — A319 i A321 — by nie zachwiać wzajemną
kompatybilnością tych modeli między innymi w zakresie używania tych samych części za-
miennych. Jeśli zmiana wprowadzona do konstrukcji któregoś z modeli powodowałaby zna-
czącą zmianę jego charakterystyki sterowniczej, konieczne byłoby odpowiednie zmodyfi-
kowanie oprogramowania komputerowej kontroli sterowania, w przeciwnym razie piloci
przeszkoleni na A320 musieliby przejść ponowne szkolenie na nowych modelach, co wiązałoby
się z dodatkowymi kosztami, nie mówiąc już o utracie wspólnej platformy i wspólnego pro-
gramu szkoleniowego. Zmodyfikowany model samolotu musi zostać następnie poddany pro-
cedurze certyfikacyjnej przez gremium autoryzacyjne, którym w USA jest Federalna Admini-
stracja Lotnicza (FAA — Federal Aviation Agency), a w Europie — Wspólne Władze Lotnicze
(JAA — Joint Aviation Authorities)2 i które decyduje o bezpieczeństwie wspomnianego modelu
oraz ewentualnej konieczności dodatkowego szkolenia pilotów i personelu serwisującego.
Ostatecznie wszystkie proponowane zmiany w stosunku do istniejącego modelu muszą zostać
drobiazgowo udokumentowane; te, które stanowią zagrożenie dla bezpieczeństwa samolotu,
jego wydajności lub „przenośności" kwalifikacji pilotów, muszą zostać zidentyfikowane i od-
rzucone. Opisane trudności wymagają od producentów i operatorów opracowania wyrafino-
wanych procedur kontroli wersji i kontroli zmian.
Mimo iż systemy informatyczne ustępują (zwykle) samolotom pod względem złożoności,
ich tworzenie wiąże się z podobnymi problemami. Muszą one funkcjonować na tyle długo, by
zwróciły się inwestycje poniesione w związku z ich powstaniem; niektóre funkcjonują nawet
dłużej — wiele z eksploatowanych dziś systemów operacyjnych linii uniksowej zawiera frag-
menty kodu stworzone jeszcze w latach 70. ubiegłego wieku. Systemy informatyczne istnieją
w wielu różnych odmianach, przykładowo wspomniany Unix doczekał się licznych wersji za-
równo dla komputerów mainframe, jak i komputerów PC i macintoshy. Wprowadzanie zmian
do eksploatowanego oprogramowania musi odbywać się szczególnie starannie, by nie pogorszyć
jego niezawodności. Skutkiem wprowadzenia waluty euro, operacji o stosunkowo niewielkiej
skali, jest wiele widocznych i znaczących zmian w wielu systemach finansowych i bizneso-
wych na całym świecie. Wreszcie, systemy informatyczne tym różnią się od samolotów, że
ewoluują znacznie szybciej, co wymaga od programistów systematycznego śledzenia zmian
i ich skutków.

2
Polska stała się członkiem JAA w listopadzie 2002 roku. JAA zakończyły działalność 30 czerwca
2009 roku, ich działalność szkoleniową kontynuuje holenderska organizacja JAA-Training Orga-
nisation — przyp. tłum.
600 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

Zarządzanie konfiguracją zmusza i programistów, i producentów samolotów do zma-


gania się z postępującymi zmianami. Pierwszą czynnością w dziele zarządzania konfiguracją
jest zidentyfikowanie elementów tej konfiguracji. Które podsystemy są podatne na zmiany?
Interfejsy których podsystemów nie powinny się zmieniać? Każdy podsystem, w którym praw-
dopodobne są zmiany, modelowany jest jako element konfiguracji, a jego stan etykietowany
jest numerem wersji. Elementami konfiguracji są oprogramowanie airbusa A320 i sterownik
portu szeregowego w systemie Unix.
Drugą funkcją zarządzania konfiguracją jest ujęcie wprowadzania zmian w ramy sfor-
malizowanego procesu. Najpierw rejestrowane jest żądanie zmiany, potem proponowana
zmiana jest analizowana pod kątem zgodności z celami projektowymi. Żądanie zmiany do-
tyczącej modelu samolotu to obszerny raport opisujący wszystkie jego podsystemy oraz ich
producentów; w przypadku prostego programu żądanie zmiany może mieć postać jednowier-
szowego e-maila. Proponowana zmiana zostaje ostatecznie zaakceptowana albo odrzucona,
zależnie od jej przewidywanych konsekwencji w postaci zmiany systemu jako całości.
Wreszcie, trzecią funkcją zarządzania konfiguracją jest gromadzenie informacji o statusie
każdej wersji każdego elementu i zależnościach między tymi wersjami. Przeglądając doku-
mentację serwisową A320, opisującą szczegółowo wersję każdego podsystemu, projektanci
nowego modelu decydują o unowocześnieniu lub zmianie poszczególnych podsystemów; czy-
tając opis najnowszej wersji sterownika portu szeregowego, szczególnie listę jego ulepszeń i zmian
w stosunku do wersji poprzednich, decydujemy albo o zainstalowaniu nowego sterownika,
albo o pozostaniu z obecnie zainstalowaną wersją.
Tradycyjnie zarządzanie konfiguracją pojmowane jest jako zadanie menedżerskie, jed-
nakże granica między tworzeniem systemu a jego pielęgnacją jest wysoce płynna i niewyraźna,
a samo zarządzanie konfiguracją pojawia się na wczesnych etapach procesu tworzenia systemu.
W tym rozdziale skupimy się właśnie na tych wczesnych fazach, skrótowo tylko traktując za-
rządzanie konfiguracją w kontekście pielęgnowania systemu. Najpierw jednak zdefiniujmy
w sposób formalny podstawowe pojęcia i koncepcje związane z zarządzaniem konfiguracją.

13.2. O zarządzaniu konfiguracją ogólnie


Zarządzanie konfiguracją to dyscyplina mająca za zadanie kontrolowanie zmian składają-
cych się na ewolucję systemu informatycznego i zarządzanie tymi zmianami [IEEE Std.
1042-1987]. Systemy zarządzania konfiguracją automatyzują identyfikację wersji, ich maga-
zynowanie i pobieranie oraz obsługę statusu konfiguracji. Na zarządzanie konfiguracją skła-
dają się następujące aktywności:

• Identyfikowanie elementów konfiguracji. Poszczególne wersje komponentów


systemu i związanych z nim produktów muszą być jednoznacznie identyfikowane
i jednoznacznie etykietowane. Programiści zaczynają identyfikowanie elementów
konfiguracji zaraz po tym, jak na podstawie umowy projektu (patrz sekcja 14.3.6)
uzgodnione zostaną zasadnicze produkty finalne i komponenty systemu. W miarę
ewoluowania systemu programiści tworzą nowe wersje i inne elementy konfiguracji.
13.2. O zarządzaniu konfiguracją ogólnie 601

• Kontrolowanie zmian. Zmiany wprowadzane do systemu i jego emisje dla użytkow-


ników są kontrolowane pod kątem spójności z celami projektowymi. Kontrola ta może
być prowadzona przez programistów, przez menedżerów lub nawet specjalny komitet,
zależnie od wymaganej jakości projektu i dynamiki wprowadzanych do niego zmian.
• Raportowanie statusu. Status każdego komponentu systemu i każdego produktu
z nim związanego oraz wszelkie żądania wprowadzenia zmian w którymś z nich są
konsekwentnie rejestrowane. Pozwala to programistom na łatwiejsze rozróżnianie
poszczególnych wersji i odnoszenie ich do konkretnych zmian. Menedżerowi po-
zwala to na śledzenie statusu projektu.
• Audyt. Wersje wybrane do emisji weryfikowane są pod kątem kompletności, spój-
ności i jakości. Weryfikacja ta przeprowadzana jest przez zespół kontroli jakości.

Ponadto kilka innych aktywności często postrzeganych jest jako część zarządzania kon-
figuracją, pisze o tym S. Dart [Dart, 1991]. Są to między innymi:

• Zarządzanie generowaniem binariów3. Większość systemów wspomagających za-


rządzanie konfiguracją umożliwia automatyczne „odbudowywanie" binariów każ-
dorazowo, gdy programiści wprowadzą zmianę do któregoś z komponentów. Systemy
takie zwykle „znają" zależności między komponentami w stopniu wystarczającym,
by sprowadzić wspomniane odbudowywanie do rozmiarów rzeczywiście koniecznych.
Systemy zarządzania konfiguracją pozwalają także na kombinowanie różnych wersji
komponentów w celu budowania różnych wariantów systemu, przeznaczonych na
przykład dla różnych platform sprzętowych i odmiennych systemów operacyjnych.
• Zarządzanie procesem. W uzupełnieniu monitorowania zmian w projekcie mogą
być także zdefiniowane określone zasady tworzenia i dokumentowania nowych wersji:
przykładowo jednym z często przyjmowanych założeń tego typu jest kompilo-
walność wersji — kod składający się na tę wersję musi być w pełni poprawny
syn taktycznie. Inne założenie może wiązać się z wymogiem budowania (pomyślne-
go) kolejnych wersji co najmniej raz na tydzień. Wiele systemów zarządzania konfi-
guracją realizuje ponadto ważną funkcję powiadamiania zainteresowanych progra-
mistów o zmianach wprowadzonych do systemu albo o załamaniu się budowania
binariów (na przykład wskutek błędu syntaktycznego w kodzie źródłowym).

3
Proces ten, określany w języku angielskim za pomocą czasownika build („budować"), polega na gene-
rowaniu binarnych wersji plików (modułów wykonywalnych, nakładek, bibliotek DLL, zasobów, pli-
ków systemu pomocy i tym podobnych) na podstawie ich pierwowzorów w postaci kodu źródłowego,
przy użyciu kompilatorów, konsolidatorów i innych podobnych narzędzi. Ponieważ w wyniku wpro-
wadzenia zmian do któregoś z komponentów zwykle tylko niektóre komponenty wymagają ponow-
nego generowania, zidentyfikowanie ich pozwala znacznie zaoszczędzić czas „budowania", dzięki
sprowadzeniu go do rozmiarów minimalnych, czyli „budowania przyrostowego" zwanego także „od-
budowywaniem". Gdy nie można przeprowadzić takiej identyfikacji, jedynym wiarygodnym sposo-
bem uzyskania spójnych binariów systemu jest przeprowadzenie „budowania" wszystkich kompo-
nentów, co nazywane jest „budowaniem zupełnym". Większość środowisk programistycznych
udostępnia narzędzia do śledzenia zależności między komponentami, co w większości przypadków
pozwala na wiarygodne stosowanie budowania przyrostowego. W sytuacji gdy budowanie binariów
odbywa się kilkadziesiąt, a nawet kilkaset razy dziennie różnica czasowa między budowaniem
przyrostowym a zupełnym nabiera wyjątkowego znaczenia praktycznego — p r z y p . tłum.
602 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

Jak już wspominaliśmy, tradycyjnie zarządzanie konfiguracją pojmowane jest jako dys-
cyplina menedżerska, wspomagająca menedżerów w kontrolowaniu zmian, obserwowaniu
statusu i audycie — informacje na ten temat znaleźć można w pracy E. H. Bersoffa, V. D.
Hendersona i S. G. Siegela [Bersoff i in.,1980] oraz w wytycznych IEEE [IEEE Std. 1042-1987]
— jednakże od niedawna zarządzanie konfiguracją rozpatrywane bywa także w kontekście
pracy programistów, którym pomaga w walce z dużą liczbą zmian, komponentów i wariantów,
o czym pisze W. A. Babich [Babich, 1986]. Ten właśnie kontekst stanowić będzie główną treść
tego rozdziału, pobieżnie potraktujemy natomiast kwestię kontrolowania zmian i obsługi statusu.
Konieczność zarządzania konfiguracją towarzyszy programistom przez cały cykl życiowy
projektu: zarządzanie to rozpoczyna się od identyfikowania elementów konfiguracji, zaraz po-
tem jak zdefiniowane zostaną elementy konfiguracji i produkty finalne, później rozciąga się
na wszystkie produkty powstające na etapach analizy, projektowania systemu, projektowania
obiektów i implementacji. W połączeniu z zarządzaniem racjonalizacją (patrz rozdział 12.
„Zarządzanie racjonalizacją") zarządzanie konfiguracją jest podstawowym narzędziem umoż-
liwiającym programistom zapanowanie nad zmianami.

13.3. Koncepcje zarządzania konfiguracją


W tej sekcji przedstawimy główne koncepcje zarządzania konfiguracją; będziemy się przy tym
wzorować na terminologii zawartej w wytycznych IEEE [IEEE Std. 1042-1987], definiującej:

• element konfiguracji (configuration item) — to produkt lub fragment oprogramo-


wania, traktowany jako odrębna całość z perspektywy zarządzania konfigurację.
Kompozycję elementów konfiguracji nazywamy agregatem zarządzania konfiguracją
(configuration management aggregate), w skrócie agregatem CM (CM aggregate).
Oprogramowanie komputera pokładowego airbusa A320 to przykład elementu kon-
figuracji; airbus A320 to przykład agregatu CM. Podobnie elementem konfiguracji
jest sterownik portu szeregowego, zaś system operacyjny Linux 4 stanowi przykład
agregatu CM.
• żądanie zmiany (change request) — to formalny raport sporządzony przez użytkow-
nika lub programistę, zawierający żądanie lub propozycję modyfikacji elementu kon-
figuracji. Przykładowe żądanie zmiany, jakim jest raport Engineering Change Pro-
posal, jeden z oficjalnych dokumentów administracji USA, jest siedmiostronicowym
formularzem [MIL Std. 480]. Ogólnie jednak żądania zmian mogą mieć postać znacz-
nie prostszą i mniej formalną, na przykład postu na grupie dyskusyjnej czy listu e-mail.
• wersję (version) — jako stan elementu konfiguracji lub agregatu CM w ściśle określonej
chwili. Dla danego agregatu CM spójny zbiór jego elementów nazywany jest konfigu-
racją (configuration). Konfiguracja może więc być uważana za wersję agregatu CM.
• warianty (variants) — jako współistniejące różne wersje elementu konfiguracji.
• promocję (promotion)— jako wersję przeznaczoną na użytek programistów.
• emisję (release) — jako wersję przeznaczoną na użytek klienta i użytkowników.

4
Linux (http://www.linux.org) to powszechnie dostępny system operacyjny, stanowiący odmianę systemu
POSIX [POSIX, 1990], stworzony przez Linusa Torvaldsa.
13.3. Koncepcje zarządzania konfiguracją 603

• bibliotekę (software library) — czyli magazyn przechowujący wersje i dostarczający


mechanizmy śledzenia statusu zmian.
• repozytorium (repository) — jako bibliotekę emisji.
• katalog główny (master directory) — jako bibliotekę promocji.

Relacje między wymienionymi koncepcjami przedstawiamy na rysunku 13.1.

Rysunek 13.1. Koncepcje zarządzania konfiguracją

13.3.1. Elementy konfiguracji i agregaty CM


Elementem konfiguracji jest produkt lub jego komponent podlegający zarządzaniu konfigu-
racją i traktowany z jej perspektywy jako odrębna całość. Przykładem elementu konfiguracji
jest oprogramowanie komputera pokładowego A320 (patrz rysunek 13.2) — w przypadku
unowocześnienia modelu podlega ono wymianie jako całość, nie można go bowiem podzielić
na komponenty, które mogłyby być instalowane niezależnie od siebie. Na podobnej zasadzie
elementem konfiguracji jest sterownik portu szeregowego w dowolnym systemie operacyj-
nym — jest zbyt prosty, by można go było podzielić na komponenty, podlegające niezależnemu
instalowaniu.

CI - Element konfiguracji
CM Aggregate - Agregat CM

Rysunek 13.2. Przykłady elementów konfiguracji i agregatów CM


się z koordynatora procesów, menedżera pamięci, wielu sterowników urządzeń, demonów

13.3.2. Wersje i konfiguracje

nego produktu powstaje przez wprowadzenie do niego jednej lub kilku zmian, polegających

potrzebnej. Konfiguracją nazywamy wersję agregatu CM.

cyjnego (na przykład FAA lub JA A), zanim dopuszczony zostanie do eksploatacji. Każda póź-

uzyskuje się pewność, że zmiany modelu nie spowodują jego odstępstwa od celów projekto-

ponentów, zaś r ó ż ^ c J m i ę d z y nimi zamykają się w niewielkiej liczbie p o d s ^ t e m ó J n i ż s z e g o

13.3.3. Żądania zmian


13.3. Koncepcje zarządzania konfiguracją 605

Rysunek 13.3. Przykłady linii bazowych, wersji i wariantów. Warianty A319, A320 i A321 bazują na tym
samym projekcie, różnią się głównie długością i kadłubem

I tak na przykład niedługo potem jak pierwsza wersja A320 uzyskała certyfikat dopusz-
czający ją do eksploatacji (w lutym 1988 roku), zrewidowana wersja A320-200 ponownie zo-
stała poddana procesowi certyfikacji. Zamierzeniem projektantów nowej wersji było zwięk-
szenie zasięgu (maksymalnego dystansu podróży) oraz zwiększenie dopuszczalnej masy startowej
(przekładające się na większą liczbę pasażerów), co miało zostać zrealizowane przez dodanie
wingletów5 na końcach skrzydeł (zmniejszenie zawirowań powietrza miało przyczynić się do
mniejszego zużycia paliwa). Koszt paliwa jest najważniejszym składnikiem kosztów eksplo-
atacji samolotu, stąd ciągła presja na producentów w kierunku jego obniżania.
Żądanie zmiany, w którym szczegółowo opisano nowe winglety, ewaluację ich wydajno-
ści oraz szacowane koszty, zostało zaakceptowane i model A320-200 uzyskał certyfikat eks-
ploatacyjny w listopadzie 1988 roku. Dla porównania, żądanie zmian w jądrze systemu opera-
cyjnego Linux miało formę e-maila wysłanego do Linusa Torvaldsa.

13.3.4. Promocje i emisje


Promocja to wersja przeznaczona na użytek programistów. Promocja oznacza wersję uważaną
na (względnie) stabilną na tyle, by mogli jej używać programiści — i tak zespoły zajmujące się
poszczególnymi podsystemami przekazują sobie nawzajem kolejne wersje podsystemów na
zasadzie promocji. Poszczególne podsystemy są też przekazywane zespołom kontroli jakości
w celu ich oceny; gdy wykryty i naprawiony zostanie błąd, zrewidowany podsystem jest prze-
kazywany do ponownej oceny.
Emisja to wersja systemu przeznaczona dla użytkownika. Do emisji kwalifikuje się ele-
ment konfiguracji spełniający kryteria określone przez zespół kontroli jakości, a więc nadający
się do oceny przez użytkowników. Przykładowo kierując emisję do beta-testerów, spodziewany

5
Winglet — aerodynamiczne skrzydełko na końcu skrzydła, mające za zadanie zmniejszenie oporu
wywołanego przez turbulencje, a w konsekwencji poprawę współczynnika doskonałości lotu i zmniej-
szenie zużycia paliwa. Patrz także http://pl.wikipedia.org/wiki/Winglet — przyp. tłum.
606 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

się wykrycia przez nich nowych usterek oraz dokonania subiektywnej oceny jakości przed-
miotowego oprogramowania. Gdy poprawione zostaną ewentualne usterki, kolejne rewizje
systemu są przekazywane do zespołu kontroli jakości, a po uzyskaniu akceptacji emitowane
ponownie do użytkowników.

13.3.5. Repozytoria i przestrzenie robocze


Biblioteka, zgodnie z definicją w standardzie [IEEE Std. 1042-1987], dostarcza mechanizmy
do przechowywania, etykietowania i identyfikowania wersji elementów konfiguracji (doku-
mentacji, modeli i kodu). Biblioteki umożliwiają także śledzenie statusu zmian dotyczących
elementów konfiguracji. Rozróżniamy trzy typy bibliotek.

• Przestrzeń robocza, zwana także bibliotekę dynamiczną, używana jest w codziennej


pracy programistów. Dostęp do niej jest nieograniczony i kontrolowany jedynie
przez poszczególnych programistów.
• Katalog główny, zwany także bibliotekę kontrolowaną, to magazyn do przechowy-
wania i śledzenia promocji. Zapisywanie w tej bibliotece nowych wersji uwarunko-
wane jest spełnieniem określonych kryteriów projektowych (na przykład poprawno-
ści syntaktycznej kodu).
• Repozytorium, zwane także bibliotekę statycznę, służy do magazynowania emisji.
Zanim dana promocja uzyska status emisji, musi spełnić określone kryteria ja-
kościowe (na przykład usunięte muszą być wszystkie usterki wykryte w ramach te-
stów regresyjnych).

Należy w tym miejscu zwrócić uwagę, że wiele obecnych systemów zarządzania konfi-
guracją określa jako „repozytorium" bibliotekę służącą do przechowywania zarówno promocji,
jak i emisji. Zamiast powyższego trójstopniowego podziału mamy więc podział dwustopniowy:
programiści tworzą nowe promocje, importując elementy konfiguracji z przestrzeni roboczej
do repozytorium, natomiast w ramach repozytorium promocje odróżniane są od emisji za
pomocą odpowiednich konwencji identyfikowania wersji.

13.3.6. Schematy identyfikowania wersji


Poszczególne wersje elementów konfiguracji rozróżniane są na podstawie unikalnych identy-
fikatorów wersji, zwanych także potocznie numerami wersji. Konkretny schemat tworzenia
takich identyfikatorów jest sprawą inwencji programistów, a o tym, że bywa ona niespożyta,
mogą świadczyć chociażby dwa następujące przykłady.

• Język programowania Ada doświadczył w swym cyklu rozwojowym licznych


przeobrażeń, których kamieniami milowymi było pięć głównych wersji oznaczonych
k o l e j n o j a k o Strawman, Woodenman, Ti nman, Ironman, Steel man 6 [Steelman, 1978].

6
DosŁ: człowiek słomiany, drewniany, cynowy, żelazny i stalowy — przyp. tłum.
13.3. Koncepcje zarządzania konfiguracją 607

• Kolejne wersje edytora TeX autorstwa D. E. Knutha [Knuth, 1986] identyfikowane


są coraz dokładniejszymi przybliżeniami dziesiętnymi liczby 71: gdy w edytorze zna-
leziony zostanie błąd (co zdarza się niezwykle rzadko), do identyfikatora wersji
dopisuje się kolejną liczbę rozwinięcia dziesiętnego. Obecna wersja identyfikowana
jest jako 3.1415926.

Na ogół jednak, wobec perspektywy częstego generowania nowych wersji, stosowane


są raczej identyfikatory wynikające z prostego algorytmu, dającego się automatyzować łatwiej
niż na przykład generowanie kolejnych przybliżeń liczby 71. Schematy takie opierają się na kil-
kuczęściowej (najczęściej dwu- lub trzyczęściowej) numeracji hierarchicznej. Rozpatrzmy dla
przykładu numerowanie kolejnych wersji edytora UML o nazwie MUE (skrót od My UML
Editor), za pomocą numeracji trzyczęściowej, odzwierciedlającej istotność poszczególnych
zmian — zmiany w podstawowej funkcjonalności, drobne usprawnienia, usunięcie usterek
(patrz rysunek 13.4). Najwyższa hierarchia to główny numer wersji (odzwierciedlający wersję
głównych elementów funkcjonalnych lub głównych elementów interfejsu użytkownika), hie-
rarchia pośrednia identyfikuje wersje pośrednie (związane z poszerzaniem lub ograniczaniem
istniejących elementów funkcjonalnych), numeracja na najniższym poziomie hierarchii wy-
nika natomiast z korygowania błędów. Zgodnie ze stosowaną praktyką, początkową emisję
przeznaczoną dla alfa- lub beta-testerów opatrujemy identyfikatorem 0.0.1.

Rysunek 13.4. Trzyczęściowy schemat identyfikowania wersji. Składnia schematu opisana jest w notacji
Backusa-Naura (BNF)

Ów prosty schemat sprawdza się jednak tylko w przypadku ściśle sekwencyjnego ge-
nerowania nowych wersji. Odstępstwem od tej sekwencyjności są gałęzie, reprezentujące
równoległe ścieżki opracowywania poszczególnych wersji, wymagające niezależnego trakto-
wania z perspektywy zarządzania konfiguracją. Co prawda, dla użytkownika kolejne emisje
608 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

zawsze rozłożone są sekwencyjnie w czasie, jednak z punktu widzenia programistów relacja


między tymi emisjami może być bardziej skomplikowana, na przykład poszczególne cechy
komponentu mogą być implementowane niezależnie przez poszczególne zespoły — jako rów-
noległe gałęzie— a później scalane w jedną spójną wersję poprzez selektywny wybór frag-
mentów z poszczególnych gałęzi.
Tak więc dla numerowania kolejnych emisji sekwencyjny schemat identyfikowania wer-
sji okazuje się całkowicie wystarczający, jednakże z punktu widzenia programistów i systemów
zarządzania konfiguracją może być niewystarczający. Oczywiście, fakt ten uwzględniony zo-
stał w systemach wspomagających zarządzanie konfiguracją. Przykładowo schemat wykorzy-
stywany przez system CVS, opisany w pracy B. Berlinera [Berliner, 1990], umożliwia jawne
reprezentowanie gałęzi i wersji — numer wersji składa się z identyfikatora gałęzi i numeru
rewizji; identyfikator gałęzi składa się natomiast z numeru wersji, od której gałąź bierze swój
początek, i unikalnego numeru identyfikującego tę gałąź. Umożliwia to programistom iden-
tyfikację gałęzi, do której należy określona wersja, oraz kolejności, w jakiej tworzone były po-
szczególne wersje związane z daną gałęzią.
Na rysunku 13.5 widzimy dwie gałęzie: główny pień (reprezentowany przez lewy pakiet
UML) i gałąź 1 . 2 . 1 wywodzącą się z wersji 1.2 (reprezentowanej przez prawy pakiet). Gałąź
ta mogła powstać na przykład w celu oceny dwóch konkurencyjnych implementacji tej samej
cechy (przykładowo obsługi przez edytor MUE diagramów interakcji UML). Zauważmy, że na
podstawie samego schematu identyfikacji wersji nie sposób dojść, w jaki sposób dwie wersje
scalane są do nowej wersji: w sytuacji przedstawionej na rysunku 13.5 scalenie wersji 1 . 2 . 1 . 2
i 1.3 daje w rezultacie wersję 2.0.

13.3.7. Zmiany i zbiory zmian


Ewolucję elementu konfiguracji można modelować na dwa sposoby:

• na podstawie stanu — poszczególne wersje reprezentowane są przez kolejne stany


elementu. Każdy stan identyfikowany jest przez numer wersji (na przykład A320-200
lub MUE 1.0). To rozwiązanie jest najczęściej spotykane w praktyce.
• na podstawie zmian — ciąg wersji reprezentowany jest w postaci linii bazowej oraz
ciągu różnic między kolejnymi wersjami, czyli na przykład dodanych albo usunię-
tych fragmentów kodu źródłowego. Wspomniane różnice zwane są popularnie przy-
rostami albo deltami. Często naprawianie usterki lub dodawanie nowej funkcjonal-
ności wiąże się z modyfikowaniem kilku elementów konfiguracji. Wszystkie zmiany
związane z tą samą rewizją grupowane są w formę zbioru zmian (change set). Jeśli
dwa zbiory zmian są rozłączne względem siebie, czyli dotyczą różnych, niepowią-
zanych ze sobą elementów, mogą być stosowane do linii bazowej w dowolnej kolej-
ności, co daje programistom większą swobodę w zakresie równoległego operowania
na konfiguracji.

Kontynuując przykład z edytorem MUE, załóżmy, że dokonaliśmy dwukrotnie zrewido-


wania linii bazowej. Wersja MUE 1.1 powstała w wyniku poprawienia błędu w obsłudze klas UML
nieposiadających operacji, zaś wersja MUE 1.2 — w wyniku poprawienia błędu w rysowaniu
13.3. Koncepcje zarządzania konfiguracją 609

Rysunek 13.5. Schemat identyfikowania wersji w systemie CVS. Składnia schematu opisana jest w notacji
Backusa-Naura (BNF). Gałęzie wywodzą swą identyfikację od wersji, z której biorą swój początek

linii przerywanych. Zbiory zmian związane z tymi rewizjami, odpowiednio emptyCl assFix:
^ChangeSet i dashedLi neFi x: ChangeSet, są niezależne o d siebie i m o g ą być z a s t o s o w a n e d o
linii bazowej w dowolnej kolejności. Konkretnie: stosując zbiór zmian emptyCl assFix do
wersji MUE 1.0, otrzymaliśmy wersję MUE 1. Ib, zaś zastosowany do tej ostatniej zbiór zmian
dashedLi neFi x dał wersję MUE 1.2. Moglibyśmy jednak zmienić tę kolejność: rewidując linię
bazową MUE 1.0 przez zbiór zmian dashedLi neFi x, otrzymalibyśmy wersję MUE 1. la, której
rewizja przez zbiór zmian emptyCl assFix dałaby w rezultacie wersję MUE 1.2. Te dwie moż-
liwości reprezentowane są przez dwie gałęziena rysunku 13.6.
Reprezentowanie ewoluującej konfiguracji w postaci ciągu zmian jest bardziej ogólne niż
jej reprezentowanie w postaci ciągu stanów, pozwala bowiem na ujęcie ciągu powiązanych
wersji różnych elementów konfiguracji w pojedynczą akcję. Co więcej, gdy zbiory zmian są
rozłączne, mogą być stosowane do kilku wersji. Właściwość ta wykorzystywana jest do „łata-
nia" wyemitowanych wersji w przypadku wykrycia w nich błędów: każda poprawka dostar-
czana jest w postaci osobnej łaty i, jeżeli zbiory zmian związane z poszczególnymi poprawkami
są rozłączne, poszczególne poprawki mogą być instalowane w dowolnej kolejności.
610 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

Rysunek 13.6. Dwie niezależne rewizje linii bazowej MUE 1.0, reprezentowane przez zbiory zmian
emptyCl assFix:ChangeSet i dashedlineFix:ChangeSet mogą być wykonane w dowolnej kolejności

13.3.8. Narzędzia wspomagające zarządzanie konfiguracją


Ponieważ rola zarządzania konfiguracją w procesie tworzenia oprogramowania jest znacząca,
pojawiło się wiele narzędzi wspomagających programistów w tym dziele. Opiszemy krótko
cztery wybrane: RCS [Tichy, 1985], CVS [Berliner, 1990], Perforce [Perforce] i ClearCase
[Leblang, 1994].
RCS (Revision Control System) to darmowe narzędzie zarządzania repozytorium prze-
chowującym wszystkie wersje konfiguracji. Programiści pobierają żądaną wersję do swojej
przestrzeni roboczej na podstawie jej identyfikatora lub daty dodania do repozytorium. Aby
zmienić wersję zapamiętanego elementu, programista musi najpierw zapewnić sobie wyłącz-
ność dostępu do niego (przez nałożenie blokady), co uniemożliwi innym programistom rów-
noczesne operowanie tym elementem; następnie pobiera element do swojej przestrzeni roboczej,
dokonuje zamierzonych poprawek i zapisuje ów element z powrotem do repozytorium, w wy-
niku czego utworzona zostanie nowa wersja i automatycznie zdjęta blokada z komponentu.
Aby zoptymalizować wykorzystanie pamięci dyskowej, RCS zapamiętuje w postaci kompletnej
tylko wersję najnowszą, pozostałe zapamiętywane są w postaci różnic między „sąsiadującymi"
wersjami. Ponieważ RCS umożliwia zarządzanie wersjami jedynie na poziomie pojedynczych
13.4. Aktywności tworzące zarządzanie konfiguracją 611

plików, kojarzenie poszczególnych wersji elementów w spójne wersje konfiguracji realizowane


jest przez programistów we własnym zakresie; dokonują oni tego przez odpowiednie etykie-
towanie wersji. System RCS nie wspiera mechanizmu gałęzi.
CVS (Concurrent Version System), również darmowy, stanowi poszerzenie RCS o kon-
cepcję gałęzi. Zamiast ciągu różnic, CVS przechowuje kompletne drzewo różnic dla każdego
elementu. Dostarcza także narzędzia do scalania dwóch gałęzi i wykrywania nierozłącznych
zbiorów zmian. CVS różni się od RCS także mechanizmem szeregowania dostępu do ele-
mentów konfiguracji: zamiast nakładania blokady na element, CVS realizuje równoległe opero-
wanie na tym samym elemencie w postaci oddzielnych gałęzi; jeśli nie nastąpiło równoległe
modyfikowanie tego samego elementu, to znaczy gdy istnieje tylko jedna gałąź, CVS bezpro-
blemowo scala ją z głównym pniem. W przeciwnym razie CVS podejmuje próbę scalenia
wszystkich gałęzi i gdy scalenia tego nie można wykonać automatycznie ze względu na nieroz-
łączność którejś pary zbiorów zmian, programista próbujący zatwierdzić zmianę jest informowa-
ny o tym fakcie. Stwarza to większe niż w przypadku RCS możliwości równoległego operowa-
nia na tym samym elemencie.
Perforce to komercyjny zastępnik RCS. Bazuje na tym samym mechanizmie repozyto-
rium, co RCS i CVS, w przeciwieństwie jednak do nich realizuje koncepcję zmian i zbiorów
zmian, co daje programistom lepszą kontrolę nad zbiorem elementów uwikłanych w kon-
kretną zmianę.
ClearCase, inne komercyjne narzędzie, dostarcza wsparcie dla koncepcji agregatów CM
i konfiguracji. Agregaty realizowane są jako katalogi, z których każdy stanowi pojedynczy
element konfiguracji z perspektywy ClearCase. Narzędzie ClearCase dostarcza także koncepcje
do definiowana wersji za pomocą reguł: reguły te mogą mieć charakter statyczny (na przykład
zaliczenie elementu w poczet konfiguracji na podstawie konkretnego numeru jego wersji)
lub dynamiczny (taki jak zaliczenie w poczet konfiguracji tylko ostatnich wersji elementów).
ClearCase oferuje także mechanizm kontroli dostępu na podstawie prawa własności do po-
szczególnych konfiguracji i ich elementów.

13.4. Aktywności tworzące zarządzanie konfiguracją


Zajmiemy się teraz aktywnościami związanymi z definiowaniem elementów konfiguracji,
promocji i emisji. Opiszemy także aktywności dotyczące obsługi gałęzi i równoległego kon-
struowania różnych wariantów elementów konfiguracji. Omówimy:

• Identyfikowanie elementów konfiguracji i agregatów CM (patrz sekcja 13.4.1),


• Zarządzanie promocjami (patrz sekcja 13.4.2),
• Zarządzanie emisjami (patrz sekcja 13.4.3),
• Zarządzanie gałęziami (patrz sekcja 13.4.4),
• Zarządzanie wariantami (patrz sekcja 13.4.5),
• Zarządzanie propozycjami zmian i ich implementowaniem (patrz sekcja 13.4.6).

Na potrzeby tej sekcji wykorzystamy w charakterze przykładu rozproszony system dys-


trybucji samochodowych części zamiennych MyCarParts. System ten umożliwia dealerom
i właścicielom samochodów przeglądanie katalogu części i ich kupowanie za pomocą zdalnych
612 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

t r a n s a k c j i . S y s t e m M y C a r P a r t s z r e a l i z o w a n y jest w a r c h i t e k t u r z e k l i e n t - s e r w e r , z d w i e m a
o d m i a n a m i s t r o n y k l i e n c k i e j : ECLi e n t , p r z e z n a c z o n e j d l a u ż y t k o w n i k ó w p r o f e s j o n a l n y c h
( m e c h a n i k ó w s a m o c h o d o w y c h ) , o r a z NC1 i e n t dla u ż y t k o w n i k ó w n o w i c j u s z y i u ż y t k o w n i k ó w
n i e o b e z n a n y c h z t e c h n o l o g i ą m o t o r y z a c y j n ą (czyli dla większości p o s i a d a c z y s a m o c h o d ó w ) .
W systemie u t r z y m y w a n e są profile u ż y t k o w n i k ó w — w y m a g a n e jest uwierzytelnianie — co
u m o ż l i w i a s t o s o w a n i e z r ó ż n i c o w a n e j polityki c e n o w e j (klient z a m a w i a j ą c y d u ż ą ilość części
o t r z y m u j e s t o s o w n y rabat), p o w i a d a m i a n i e w y b r a n y c h k l i e n t ó w o i n t e r e s u j ą c y c h ich n o w o -
ściach, a także optymalizację wykorzystania sieci. S t r u k t u r a p o d s y s t e m ó w s y s t e m u m y C a r P a r t s
p r z e d s t a w i o n a jest n a r y s u n k u 13.7 i w tabeli 13.1.

Rysunek 13.7. Odwzorowanie sprzętowe k o m p o n e n t ó w systemu myCarParts

Tabela 13.1. Opis k o m p o n e n t ó w systemu myCarParts z rysunku 13.7

: Deal erPC K o m p u t e r dealera służący do zamawiania części, posiadający możliwość


szerokopasmowego połączenia z serwerem.

: Ecl i ent Podsystem zlokalizowany na komputerze : Deal erPC, oferujący doświadczonemu


użytkownikowi (mechanikowi lub dealerowi) możliwość wyszukiwania części
zamiennych na podstawie ich identyfikatorów, marki i roku produkcji samochodu
oraz historii dotychczasowych zamówień.

: HomePC Komputer osobisty właściciela samochodu, służący do zamawiania części, posiadający


możliwość łączenia się z serwerem poprzez m o d e m .
: NC1 i ent Podsystem zlokalizowany na komputerze : HomePC, oferujący użytkownikowi
niezaawansowanemu (przeciętnemu właścicielowi samochodu) wyszukiwanie
części zamiennych na podstawie ich opisu, a w przyszłych wersjach — na podstawie
mapy rozmieszczenia części w samochodzie.

: ServerHost Część serwerowa systemu, hostująca katalog części zamiennych.

: Server Komputer śledzący aktywność klienta, udostępniający m u listę części na podstawie


podanych kryteriów wyszukiwania i realizujący zamówienia.

T w o r z e n i e i e w o l u o w a n i e s y s t e m u myCarParts w y m a g a z a c h o w a n i a o k r e ś l o n e j k o o r -
dynacji m i ę d z y poszczególnymi jego k o m p o n e n t a m i .
13.4. Aktywności tworzące zarządzanie konfiguracją 613

• Protokół komunikacji między klientem a serwerem może być okazjonalnie uaktual-


niany. Ponieważ wielu klientów wciąż posługiwać się będzie starszymi wersjami
podsystemów klienckich (EC1 ient i NC1 ient), konieczne jest zachowanie kompaty-
bilności serwera ze wszystkimi wersjami tych podsystemów i testowanie każdej re-
wizji protokołu w kontekście tych wersji.
• Nowe wersje podsystemów klienckich mogą implementować elementy funkcjonal-
ności nieobecne w starszych wersjach strony serwerowej. Uaktualnienie oprogra-
mowania serwera musi więc nastąpić w pierwszej kolejności, przed przekazaniem
klientom nowych wersji ich podsystemów.
• Nowe wersje podsystemów klienckich dostępne będą w formie uaktualnień, które
klienci będą mogli pobierać i instalować. Uaktualnione (papierowe) podręczniki wy-
syłane będą klientom w sposób tradycyjny (pocztą lub przesyłką kurierską). W sytu-
acji gdy w krótkim okresie czasu skumulowanych zostanie kilka uaktualnień, klient
musi mieć możliwość łatwego przyporządkowania otrzymywanych podręczników
do poszczególnych wersji.

Załóżmy więc na przykład, że system myCarParts w początkowej wersji umożliwia


klientom wyszukiwanie potrzebnych części jedynie na podstawie informacji tekstowej — iden-
tyfikatora części, jej nazwy, opisu, marki i roku produkcji samochodu oraz odnośników do
innych współpracujących z nią części. Funkcjonalność ta jest całkowicie wystarczająca dla klienta
profesjonalnego, może być jednak mało przydatna klientowi nieznającemu nazw i identyfi-
katorów poszczególnych części. Dla rozwiązana tego problemu w drugiej emisji systemu
przewidziano dla użytkowników nawigowalną mapę części zamiennych, umożliwiającą użyt-
kownikowi ich identyfikowanie na podstawie wyglądu i umiejscowienia w konstrukcji samo-
chodu. Zgodnie z opisanymi wcześniej wymogami, wprowadzenie tej funkcjonalności musi
odbywać się w postaci następujących, wykonywanych kolejno kroków. Oto one.

1. Rozszerzenie oprogramowania serwera o możliwość zarządzania mapami części.


2. Wyemitowanie i zainstalowanie zmodyfikowanego oprogramowania serwerowego.
3. Utworzenie bazy map, po jednej dla każdej marki i rocznika samochodu.
4. Dodanie do podsystemu NC1 ient nowego interfejsu, umożliwiającego posługiwanie
się wspomnianymi mapami.
5. Wyemitowanie i zainstalowanie nowej wersji systemu NC1 i ent.

Opisane wzbogacenie systemu o mapy części zamiennych stanowić będzie bazę dla na-
szych kolejnych przykładów.

13.4.1. Identyfikowanie elementów konfiguracji i agregatów CM


Identyfikowanie elementów konfiguracji i agregatów CM odbywa się głównie po sporządze-
niu umowy projektu, w ramach której uzgodniony zostaje z klientem zbiór produktów final-
nych, oraz po zakończeniu etapu projektowania systemu, gdy znany jest jego podział na pod-
systemy. Identyfikowanie elementów konfiguracji i agregatów CM rozciąga się jednak także
na pozostałe fazy realizacji projektu, w trakcie których modyfikowany może być zarówno
614 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

wspomniany zbiór uzgodnionych produktów finalnych, jak i kształt dekompozycji systemu.


Podobnie jak identyfikowanie obiektów modelu analitycznego, identyfikowanie elementów
konfiguracji i ich agregatów nie jest czynnością algorytmiczną, po części dlatego, że niektóre
z nich są ewidentne (jak dokumenty RAD czy SDD), a po części ze względu na subtelność
niektórych innych (takich jak definicje protokołów komunikacyjnych). Elementy konfiguracji
mają postać fragmentów kodu źródłowego lub samowystarczalnych dokumentów, których
ewoluowanie musi być śledzone i kontrolowane przez cały cykl rozwojowy projektu.
W przypadku systemu my Car Parts za element konfiguracji uważać będziemy (między
innymi) każdy dokument mający status produktu finalnego — zatem zgodnie z umową projektu:

• wszystkie dokumenty przeznaczone dla użytkowników, czyli RAD i podręczni-


ki użytkownika profesjonalnego i nieprofesjonalnego (odpowiednio EC! ient UM
iNClient UM),
• dokumenty systemowe, głównie SDD i ODD,
• kod źródłowy podsystemów widocznych na rysunku 13.7 oraz różnych programów
instalacyjnych.

Do elementów konfiguracji zaliczymy także interfejsy współpracy między podsystemami


— ich zmiany muszą być starannie kontrolowane — natomiast w ramach dokumentu SDD
wyróżnimy dwa elementy konfiguracji: specyfikację protokołu komunikacji klientów z serwe-
rem oraz opis schematu bazy danych.
Wynik podziału systemu myCarParts na elementy konfiguracji i agregaty CM widoczny
jest na rysunku 13.8. Ponieważ poszczególne podsystemy mogą być emitowane niezależnie,
dla każdego systemu wyróżniliśmy jeden agregat CM grupujący elementy konfiguracji nale-
żące do tego podsystemu — i tak na przykład modyfikując podsystem NC1 i ent w związ-
ku z wprowadzeniem do niego obsługi map, musimy rozszerzyć dokument analizy wymagań
(RAD) o nowe przypadki użycia, zmodyfikować podręcznik użytkownika nieprofesjonalnego
(NC1 i ent UM) o opis posługiwania się mapami, dodać do dokumentu projektu obiektów (ODD)
systemu NC1 ient opis nowych klas związanych z obsługą map i — oczywiście — zaimple-
mentować w kodzie źródłowym oraz przetestować nową funkcjonalność 7 . W związku z po-
wyższym, agregat NCI i ent: CM Aggregate podlega zrewidowaniu w postaci włączenia do jego
nowej wersji zmodyfikowanych wersji elementów składowych; zauważmy, że odbywa się to
bez wpływu na podsystem EC1 i ent i agregat EC1 i e n t : CM Aggregate, bowiem elementy obu
tych podsystemów są od siebie niezależne.
Trzy podsystemy tworzące system myCarParts nie są jednak całkiem niezależne od
siebie. Zanim bowiem rozszerzymy podsystem NC1 i ent o obsługę map, musimy najpierw po-
szerzyć podsystem Server o funkcje przechowywania i wyszukiwania tych map. Mimo iż obie
te funkcjonalności realizowane są niezależnie, musimy mieć pewność, że ich emisje będą od-
powiednio skoordynowane, czyli że uaktualnienie serwera dokonane zostanie przed wy-
emitowaniem wersji klienckiej. Dlatego właśnie definiujemy na poziomie systemu agregaty
CM odpowiadające trzem wspomnianym podsystemom. Agregaty RAD: CM Aggregate, SDD: CM

7
Dla prostoty pominęliśmy na diagramie elementy r e p r e z e n t u j ą c e kod źródłowy, przypadki testowe
i podręcznik testera.
13.4. Aktywności tworzące zarządzanie konfiguracją 615

Rysunek 13.8. Elementy konfiguracji i agregaty CM systemu myCarParts

Aggregate i ODD: CM Aggregate reprezentują spójne wersje produktów finalnych, przykładowo


w skład agregatu ODD: CM Aggregate zawierającego uaktualniony element NCI i ent ODD:CI,
opisujący klasy związane z obsługą map, wchodzi również element Server ODD: CI zawierający
opisy klas odpowiedzialnych za przechowywanie i wyszukiwanie tych map.

13.4.2. Zarządzanie promocjami


Programiści tworzą nowe promocje elementów konfiguracji w celu przekazywania ich innym
programistom, by na przykład dokonali przeglądu albo na użytek debugowania innych ele-
mentów. Gdy utworzona promocja zostaje zapisana w repozytorium, zainteresowani nią pro-
gramiści mogą ją stamtąd pobierać, a programista będący jej autorem może nadal zajmować
616 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

się rozwijaniem odnośnego elementu konfiguracji bez kolizji z pracami kolegów. Zapisana
w repozytorium promocja nie może być modyfikowana, uskutecznienie zmian wprowadzo-
nych przez programistę do elementu konfiguracji wymagać będzie utworzenia nowej promocji.
Spójrzmy na rysunek 13.9, przedstawiający migawki z czterech różnych przestrzeni
roboczych, w związku z realizacją następującego scenariusza wynikającego z rozszerzenia
klienta NC1 ient o mechanizm map. Programiści rozpoczynają od modyfikacji modelu anali-
tycznego, do którego dodają przypadki użycia związane z nawigowaniem po mapach oraz
modyfikują odpowiednio niektóre z istniejących przypadków użycia; efektem tych zmian jest
promocja NC1 i ent RAD .2.0. W następnej kolejności modyfikowany jest model projektu sys-
temu, w którym uwzględnione zostaje przechowywanie i wyszukiwanie map — w rezultacie
otrzymujemy promocje Protokó?.2.0 i Schemat danych.2.0. Potem dokonywana jest pierw-
sza implementacja serwerowej części zmodyfikowanego protokołu komunikacyjnego, której
efektem jest promocja Server .2.0, przeznaczona do współpracy z promocją NC1 i ent .2.0
(i wyższymi wersjami), w celu jej przetestowania. Testy wykonane przez użytkowników
doprowadzają do wykrycia kilku błędów w implementacji serwera; programiści odnajdują
i usuwają przyczyny tych błędów, czego efektem są promocje S e r v e r . 2 . 1 i Server.2.2.
W międzyczasie zespół dokumentacyjny dokonuje rewizji podręcznika użytkownika NC1 i ent
UM.2.0 w oparciu o NC1 i ent RAD .2.0. Zauważmy, że równolegle z powyższymi aktywno-
ściami zespół zajmujący się podsystemem EC 1 i ent może usuwać tkwiące w nim usterki, a do
jego testowania wykorzystywać starszą, stabilną wersję serwera (Server. 1.4). Jak widać, nawet
jeśli poszczególne zespoły zmierzają w kierunku zbudowania spójnego systemu, mogą po-
sługiwać się różnymi promocjami tego samego komponentu do czasu, aż postać wszystkich
komponentów ustabilizuje się.
Promocje reprezentują stan elementów konfiguracji w momencie udostępniania ich
programistom. Zwykle wymaga się, by były one wolne od błędów syntaktycznych (czyli dawały
się skomplikować), a kilka pomniejszych ograniczeń szczegółowo określa zasady przekazywa-
nia produktów między zespołami. Gdy jakość elementów konfiguracji zyska odpowiednio wy-
soki stopień, potwierdzony przez zespół kontroli jakości, promocjezyskują możliwość awan-
sowania do rangi emisji.

13.4.3. Zarządzanie emisjami


Utworzenie nowej emisji elementu konfiguracji lub agregatu CM jest decyzją menedżerską,
podyktowaną zazwyczaj zarówno potrzebami marketingowymi, jak i jakością wspomnianych
elementów. Emisja taka może oferować nową funkcjonalność, lecz jej celem może być rów-
nież wyeliminowanie krytycznych błędów.
Mimo iż tworzenie emisji odbywa się w sposób podobny do tworzenia promocji, jest
jednak procesem bardziej skomplikowanym i bardziej kosztownym. W przypadku promocji,
jeżeli nowa wersja komponentu przysparza więcej kłopotów niż miała w założeniu elimino-
wać, programiści mogą po prostu powrócić do poprzedniej promocji; nieadekwatny podręcznik
użytkownika nie stanowi dla nich problemu, znają przecież dobrze system, nad którym pracują.
Użytkownicy, dla których przeznaczone są emisje, plasują się na innej pozycji: nie są zain-
teresowani testowaniem oprogramowania, to ono ma wspomagać ich pracę. Błędy w systemie
lub niespójna dokumentacja przeszkadzają w spełnieniu tego celu i kierują zainteresowanie
13.4. Aktywności tworzące zarządzanie konfiguracją 617

Rysunek 13.9. Migawki z przestrzeni roboczej różnych programistów pracujących nad systemem
myCarParts. Zespoły podsystemów NCI i ent i Server oraz zespół dokumentalistów zajmują się wpro-
wadzeniem nawigowalnych m a p do systemu, zespół podsystemu EClient posługuje się natomiast
starszą, stabilną wersją serwera. Dla wszystkich elementów konfiguracji wersje l.x odnoszą się do
promocji nieoferujących mechanizmu map, natomiast wersje 2.x oferują częściową lub kompletną im-
plementację tego mechanizmu

sfrustrowanych użytkowników w stronę produktów konkurencji. Jest wobec tego zrozumiałe,


że emisje tworzone są w warunkach znacznie bardziej rygorystycznych niż te, które towarzyszą
kreowaniu promocji. W celu zapewnienia należytej jakości i spójności zespół kontroli jakości
ocenia jakość poszczególnych komponentów emisji i koordynuje proces rewizji tych, w których
stwierdzono występowanie usterek. Ów audyt odbywa się przy minimalnej ingerencji w „nor-
malną" pracę programistów.
Rozpatrzmy dla przykładu emisję wersji systemu myCarParts oferującej mechanizm map
(patrz rysunek 13.10). Rozpoczynamy od utworzenia stabilnych promocji NC1 i ent .2.4
i Server.2.3 implementuj ących tę funkcjonalność. Zespół kontroli jakości testuje promocję
NC1 i e n t . 2 . 4 w kontekście najnowszej wersji dokumentu RAD (NC1 i ent RAD.2.0) i znajduje
błąd, który programiści poprawiają, tworząc kolejną promocję — NC1 i e n t . 2 . 5 . Przy okazji
zespół kontroli jakości testuje także aktualną wersję podsystemu EC1 ient (EC1 i e n t . 1.5 — nie
618 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

pokazaliśmy tego na rysunku) we współpracy z serwerem w wersji Server.2.3 i nie wykrywa


żadnych usterek. Decyduje więc o włączeniu do kolejnej emisji komponentów NC1 i ent .2.5,
E C l i e n t . l . 5 i S e r v e r . 2 . 3 . To jeszcze nie koniec: zespół kontroli jakości weryfikuje także naj-
nowszy podręcznik użytkownika (NC1 i ent UM. 2.0) pod kątem spójności z wersją NC1 i ent. 2.5.
Poprawienie kilku napotkanych błędów doprowadza do powstania wersji NClient UM.2.1,
która włączona zostaje w skład konstruowanej emisji. Emisja ta kierowana jest do użyt-
kowników testujących, którzy napotykają na dwa istotne błędy we współpracy serwera z pod-
systemem NC1 ient; efektem ich poprawienia są wersje NC1 i e n t . 2.6 i Server. 2.4. Oprogra-
mowanie testowane jest ponownie i emitowane do użytkowników jako wersja myCarParts .2.0.

Rysunek 13.10. Proces konstruowania emisji m y C a r P a r t s . 2 . 0 oferującej funkcjonalność nawigo-


walnych m a p
13.4. Aktywności tworzące zarządzanie konfiguracją 619

Administrator systemu myCarParts uaktualnia oprogramowanie serwera oraz schemat


jego bazy danych i obserwuje — w ramach testów systemu — wystąpienie ewentualnych pro-
blemów we współpracy nowego oprogramowania serwera ze starszymi podsystemami klienc-
kimi. Jeśli problemy takie nie wystąpią, administrator udostępnia wersję NC1 i ent . 2 . 6 tym
użytkownikom, którzy zainteresowani są nową funkcjonalnością map. Gdy użytkownicy ci
potwierdzą bezbłędność podsystemu, wszyscy użytkownicy zachęcani są do zainstalowania
nowej wersji; starsza wersja serwera zostaje uznana za niebyłą, a starsze wersje podsystemu
NC1 ient pozbawione zostają wsparcia i serwisu. Nowe wersje podsystemów i dokumentacji
zostają przekazane użytkownikom albo w formie łat, albo w formie kolejnej głównej wersji.
Proces tworzenia emisji koncentruje się na ich jakości i spójności — zespół kontroli ja-
kości pełni rolę „bramkarza" między programistami i użytkownikami. Typowi użytkownicy
oczekują, że oprogramowanie ułatwi im wykonywanie pracy, nie są więc zainteresowani te-
stowaniem ani debugowaniem tego oprogramowania. Sprawy mają się jednak nieco inaczej,
gdy użytkownicy są jednocześnie programistami, wówczas bowiem tworzą sami dla siebie po-
kaźną porcję narzędzi różnego charakteru: od prostych skryptów ułatwiających wykonywanie
powtarzalnych zadań, po pełnoprawne środowiska programistyczne, edytory świadome składni
czy systemy zarządzania konfiguracją. Narzędzia takie, zwłaszcza gdy zdolne są rozwiązywać
powszechnie występujące problemy, zyskują sobie popularność i rozpowszechniane są za po-
średnictwem internetu. „Do ściągnięcia" są już dostępne nie tylko zaawansowane kompilatory
i środowiska programistyczne, ale i kompletne systemy operacyjne, z kilkoma dystrybucjami
Linuksa na czele. Zatraca się w ten sposób podział na programistów i użytkowników, wszyscy
chętni są do tworzenia, testowania i debugowania, by jak najszybciej uzyskiwać bezbłędne,
coraz bardziej funkcjonalne wersje. Wykształca się w ten sposób swoisty model tworzenia
oprogramowania, zwany popularnie modelem bazarowym ([Raymond, 1998]); tu promocje
praktycznie nie różnią się niczym od emisji.

13.4.4. Zarządzanie gałęziami


Przedstawiliśmy już zarządzanie promocjami i emisjami w kontekście pojedynczej zmiany:
różne elementy konfiguracji poddawane są rozmaitym modyfikacjom, których skutki oceniane
są przez zespół kontroli jakości. W procesie tym musimy radzić sobie z trudnościami wyni-
kającymi z konieczności utrzymania spójności między powiązanymi promocjami i minimali-
zowania ryzyka wprowadzenia nowych usterek. Dotychczas zakładaliśmy jednak, że cały ten pro-
ces odbywa się jednowątkowo.
Zwykle jednak programiści pracują równolegle nad wieloma zmianami i usprawnie-
niami: przykładowo w czasie, gdy jeden z podzespołów zajmuje się poszerzeniem podsystemów
NCI i ent i Server o nawigowalne mapy, inny może zająć się poszerzeniem podsystemu EC1 i ent
o rejestrowanie historii zapytań formułowanych przez użytkownika. W ten sposób oba ze-
społy pracują, w odseparowaniu od siebie, nad różnymi konfiguracjami tych samych grup
podsystemów. Podejście to można jednak zastosować tylko wtedy, gdy oba zestawy zmian nie
zazębiają się, a interfejsy podsystemów zachowują kompatybilność wstecz. Gdy dwie reali-
zowane równocześnie zmiany dotyczą tego samego komponentu, wymagane jest podejście
zgoła odmienne — „rozgałęziający się" proces implementacji w pewnym momencie zbiega się
ponownie: niezależnie wprowadzane zmiany scalane są do jednej spójnej postaci.
620 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

Jako przykład weźmy dwie nakładające się zmiany: podczas gdy jeden zespół zajmuje
się wprowadzaniem do podsystemów NCI i ent i Server funkcjonalności związanej z nawi-
gowalnymi mapami (o czym pisaliśmy w sekcjach 13.4.2 i 13.4.3), drugi zespół pracuje nad
skróceniem czasu reakcji serwera (Server). Oba zespoły, by zrealizować swe cele, muszą
zmodyfikować oba podsystemy, w tym ich interfejsy.
Zmiany te przyporządkowane zostały odrębnym zespołom ze względu na zróżnicowane
ryzyko, jakie niosą ze sobą: dodanie nawigowalnych map jest dobrze zdefiniowane i stanowi
poszerzenie dotychczasowej funkcjonalności, bez jakiegokolwiek uszczerbku dla jej obecnej
postaci. Dla odmiany, wysiłki zmierzające do zoptymalizowania reaktywności serwera mają
w dużej mierze charakter eksperymentalny i stanowią raczej wyprawę w nieznane: programiści
muszą zidentyfikować podstawowe źródła „wąskich gardeł" wydajności i zaprojektować heu-
rystyki zmierzające do usprawnienia realizacji najbardziej typowych żądań. Efekty tych dążeń
muszą zostać następnie ocenione zarówno pod kątem rzeczywistych osiągów, jak i (ewentual-
nych) zagrożeń dla stabilności oraz utrudnień w utrzymaniu serwera. Ponadto rozdzielenie
obu zmian stwarza bardziej elastyczne możliwości w zakresie dystrybucji: jeśli optymalizowanie
reaktywności serwera zostanie ukończone stosunkowo wcześnie, można ją uwzględnić w wersji
podsystemu NC1 ient przekazywanej klientowi, w przeciwnym razie możną ją uczynić przed-
miotem późniejszych poprawek rozpowszechnianych w formie łaty.
Aby umożliwić niezależne realizowanie obu zmian, musimy utworzyć gałąź rozpoczy-
nającą się w ostatniej promocji obu podsystemów (patrz rysunek 13.11). Zespół odpowie-
dzialny za nową funkcjonalność kontynuuje pracę w ramach głównego pnia (wychodząc od
wersji Server. 1.5 i NC1 i ent .1.6), zaś zespół odpowiedzialny za usprawnienie wydajności
serwera tworzy gałąź (zaczynającą się od wersji Server. 1.5.1.1) 8 , a jednocześnie postanawia
ograniczyć swe zmiany wyłącznie do podsystemu Server bez zmieniania jego interfejsu. Oba
zespoły pracują odtąd niezależnie, aż do ukończenia implementacji obu zmian. Implementacja
nawigowalnych map zostaje ukończona wcześniej, a jednym z jej efektów jest wersja Server . 2 . 4
(co widzieliśmy już na rysunku 13.10). Wkrótce potem kończy swą pracę drugi zespół,
produkując wersję Server. 1.5.1.7, która charakteryzuje się czterokrotnie mniejszym czasem
dostępu dla typowych żądań za cenę nieznacznego tylko rozszerzenia protokołu komuni-
kacyjnego. Stajemy więc przed koniecznością zintegrowania obu zmian do spójnej wersji
systemu myCarParts, łączącej nawigowalne mapy i sprawniejszą komunikację klientów
z serwerem.
Scalanie gałęzi wspomagane jest narzędziami dedykowanymi zarządzaniu konfiguracją,
dokonującymi próby scalania ostatnio dokonywanych zmian. Gdy wykryta zostanie zależność
między poszczególnymi zmianami, to znaczy gdy dwie lub więcej dotyczą tej samej klasy czy
metody, wymagana jest interwencja programisty, który — poinformowany za pomocą odpo-
wiedniego komunikatu (alertu) — musi ręcznie dokonać rozwiązania zaistniałego konfliktu.
W naszym przykładzie konflikt ten dotyczy klasy DBInterface, modyfikowanej przez oba ze-
społy (patrz rysunek 13.12).
Zespół zajmujący się nawigowalnymi mapami utworzył nową metodę processMap
^Request () realizującą żądanie wyszukania odpowiedniej mapy w bazie. Drugi zespół zbu-
dował natomiast metodę processPartRequest () wyszukującą w bazie część zamienną na

8
Stosujemy identyfikowanie wersji i gałęzi zgodnie ze schematem przyjętym w systemie CVS [Berliner,
1990].
13.4. Aktywności tworzące zarządzanie konfiguracją 621

Rysunek 13.11. Przykład gałęzi. W ramach głównego pnia programiści implementują w systemie
myCarParts nawigowalne mapy. W równoległej gałęzi inni programiści optymalizują wydajność
serwera i jego bazy danych. Pierwsza z tych zmian zostaje zrealizowana wcześniej i udostępniona
klientom jako emisja, druga zmiana zostaje udostępniona później jako łata

podstawie jej identyfikatora. Scalona wersja klasy DBInterface powstała poprzez wybranie
klasy processMapRequest() z głównego pnia i klasy processPartRequest () z gałęzi. Tak zre-
widowana klasa — w postaci promocji Server.2.5 — poddana została testom w celu zapew-
nienia, że rozwiązane zostały wszystkie konflikty między zmianami. Zastosowany w metodzie
processPartRequest()mechanizm cache'owania okazał się na tyle skuteczny, że programiści
postanowili go użyć także w metodzie processMapRequest(), co ich zdaniem powinno
wydatnie przyczynić się do dalszego skrócenia czasu odpowiedzi serwera; po odpowied-
nim zmodyfikowaniu metody i przetestowaniu pracy serwera z jej nową wersją utworzyli
nową promocję Server.2.6.

Heurystyki wspomagające zarządzanie gałęziami

Jak widzieliśmy na przykładzie, scalanie dwóch gałęzi nie jest wcale operacją oczywistą.
Wymaga zwykle wsparcia ze strony narzędzi zarządzania konfiguracją oraz (co ważniejsze) zna-
czącej interwencji programistów, a wersja będąca wynikiem scalenia wymaga zaawansowane-
go przetestowania weryfikującego poprawność tego scalenia. W zaprezentowanym przykładzie
622 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

Rysunek 13.12. Przykład scalania konfliktujących zmian w klasie D B I n t e r f a c e

scaleniu podlegały dwie proste zmiany, w dodatku ograniczone do jednego podsystemu (Server)
i niezmieniające jego interfejsu. Tego rodzaju ograniczenia zmniejszają prawdopodobieństwo
pojawienia się zależności między zmianami; gdy wprowadzanie zmian w ramach poszczegól-
nych gałęzi odbywa się żywiołowo, bez żadnych ograniczeń, rozbiegają się one tak dalece, że
ich scalenie okazuje się niewykonalne. I choć nie istnieje żadna recepta na skuteczne rozwią-
zywanie tego problemu, przestrzeganie kilku prostych zasad (heurystyk) może przyczynić się do
zmniejszenia rozbieżności między gałęziami.

• Zidentyfikuj prawdopodobne zależności między zmianami. Gdy zapoczątkowana


zostanie gałąź (gałęzie), ale zanim jeszcze rozpoczną się prace projektowe i imple-
mentacyjne w jej ramach, programiści powinni postarać się antycypować ewentualne
konflikty między zmianami. Dzięki takiemu rozpoznaniu łatwiej będzie można okre-
ślić ograniczenia minimalizujące wspomniane konflikty, na przykład żądanie nie-
zmienności interfejsów klas, które mogą w tych konfliktach uczestniczyć.
• Wykonuj częste scalania. Polityka zarządzania konfiguracją może zobowiązywać pro-
gramistów do częstego scalania gałęzi z najnowszą wersją głównego pnia — raz dzien-
nie, raz na tydzień albo każdorazowo, gdy pojawi się nowa promocja. Scalanie powinno
odbywać się zawsze w ramach gałęzi, bez propagowania do głównego pnia. Polityka
13.4. Aktywności tworzące zarządzanie konfiguracją 623

zarządzania konfiguracją może także łagodzić pewne wymagania, na przykład żądać


jedynie syntaktycznej poprawności wersji będącej wynikiem scalenia, a nie bezwzględ-
nego rozwiązania wszystkich konfliktów wynikających ze scalania. Ma to zachęcić
programistów zarówno do wczesnego przewidywania samych konfliktów, jak i ob-
myślania zawczasu sposobów ich rozwiązywania.
• Komunikuj przewidywane konflikty. Mimo iż zespoły realizujące poszczególne gałęzie
mogą pracować niezależnie od siebie, powinny jednak przewidywać możliwe kon-
flikty między zmianami i komunikować je pozostałym zespołom. Uboczną korzyścią
tej strategii jest wypracowanie w powiązanych zespołach odpowiednich ograniczeń,
które sprawią, że przyszłe scalanie będzie mniej problematyczne.
• Minimalizuj zmiany w głównym pniu. Ryzyko konfliktów między zmianami jest tym
mniejsze, im mniejszy zakres tych zmian. Dobrym (choć nie zawsze akceptowalnym)
ograniczeniem jest ograniczenie zmian dokonywanych bezpośrednio na głównym
pniu wyłącznie do tych, które wynikają z poprawiania błędów; pozostałe zmiany
powinny być dokonywane w ramach gałęzi.
• Minimalizuj liczbę gałęzi. Gałęzie stanowią mechanizm wygodny, ale i skompliko-
wany, dlatego nie powinny być nadużywane. Wysiłek związany ze scalaniem nieroz-
ważnie generowanych gałęzi może zupełnie niweczyć wszelkie korzyści wynikające
z ich stosowania. A stosowanie to ma sens tylko wtedy, jeśli jest rzeczywiście opła-
calne i konflikty wynikające ze scalania dają się rozwiązywać za pomocą rozsądnego
wysiłku.

Tak czy inaczej, decyzja o utworzeniu gałęzi jest istotnym zdarzeniem w realizacji projektu,
powinna być więc starannie przemyślana, zaplanowana i zaaprobowana przez menedżera.

13.4.5. Zarządzanie wariantami


Warianty to wersje przewidziane do współistnienia. Poszczególne warianty systemu mogą róż-
nić się pod względem platformy docelowej (sprzętu lub systemu operacyjnego) bądź pod
względem funkcjonalności (wersja standardowa, wersja profesjonalna, wersja dla nowicjuszy,
wersja dla ekspertów). Realizacja systemu wielowariantowego może odbywać się w jeden
z dwu poniższych sposobów (patrz rysunek 13.13).

• Zrównoleglone projekty. Każdy wariant powierzany jest do realizacji całkowicie jed-


nemu zespołowi. Wszystkie zespoły pracują w oparciu o ten sam zestaw wymagań,
każdy z nich odpowiedzialny jest za zaprojektowanie, zaimplementowanie i przete-
stowanie swojego wariantu. Współdzieleniu między zespoły podlega niewielka liczba
elementów konfiguracji, głównie dokument RAD i podręcznik użytkownika.
• Pojedynczy projekt. Dekompozycja systemu na podsystemy dokonywana jest z myślą
o jak największym wykorzystaniu wspólnego kodu. Kod specyficzny dla poszcze-
gólnych wariantów zamknięty zostaje bądź to w ramach niewielkiej liczby podsys-
temów niskiego poziomu, bądź też realizowany jest w postaci odrębnych podsyste-
mów całkowicie od siebie niezależnych.
624 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

Rysunek 13.13. Przykłady dwóch strategii realizacji systemu wielowariantowego. W przypadku zrówno-
leglonych projektów warianty podsystemu NC1 i ent dla macintosha i dla peceta realizowane są cał-
kowicie niezależnie od siebie, w przypadku pojedynczego projektu realizowane są jako uniwersalne
podsystemy, oferujące zróżnicowane interfejsy GUI i TCP dla każdej z platform

Strategia zrównoleglonych projektów prowadzi do pewnej liczby prostszych projek-


tów opierających się na tej samej specyfikacji wymagań; strategia pojedynczego projektu opiera
się natomiast na współdzieleniu podsystemu zawierającego kod wspólny dla wszystkich wa-
riantów. Na pierwszy rzut oka pierwsza z nich wiąże się z dużą porcją redundancji, ponieważ
ta sama funkcjonalność projektowana jest i implementowana wielokrotnie; strategia poje-
dynczego projektu wydaje się bardziej efektywna, jako że spora porcja kodu współdzielona
jest przez wszystkie warianty, co skutkuje ogólnie lepszym projektem. Nieoczekiwanie jednak
strategia zrównoleglonych projektów wybierana jest często przy realizacji projektów ko-
mercyjnych, w celu uniknięcia komplikacji organizacyjnych, o czym pisze C. F. Kemerer
[Kemerer, 1997]. Współdzielenie kodu między warianty wiąże się bowiem z kilkoma proble-
mami. Oto najważniejsze.

• Jeden producent, wielu konsumentów. Poszczególne warianty systemu cechują się


zróżnicowanymi wymaganiami; zestaw współdzielonych podsystemów, by sprostać
im wszystkim, musi więc być wystarczająco ogólny.
• Długi cykl realizacji żądania zmiany. Zmiana we współdzielonym podsystemie, pro-
ponowana przez jeden z zespołów, musi zostać skonfrontowana z realiami wszystkich
wariantów przed jej zweryfikowaniem i zaimplementowaniem, co wymaga odpo-
wiedniego czasu, dłuższego niż w przypadku pozostałych zmian.
13.4. Aktywności tworzące zarządzanie konfiguracją 625

• Niespójności między platformami. Współdzielone podsystemy mogą narzucać na


poszczególne warianty ograniczenia, które kolidują z ograniczeniami platform wła-
ściwych dla tych wariantów. Przykładowo współdzielone systemy mogą być skon-
struowane zgodnie z wielowątkowym modelem przepływu sterowania, podczas gdy
narzędzia do tworzenia interfejsu użytkownika (dla danego wariantu) zakładają, że
będzie sterowany zdarzeniami.

Powyższe problemy stanowią dla programistów mocny argument na rzecz ucieczki od


współdzielenia podsystemów i opracowywania całości wariantów we własnym zakresie. Tym-
czasem problemy te można skutecznie rozwiązywać, przewidując ich konkretną postać już na
etapie projektowania systemu i odpowiednio zarządzając jego konfiguracją. Dobry projekt
systemu powinien być elastyczny pod wieloma względami, między innymi właśnie pod wzglę-
dem jego wielowariantowości; oznacza to dekompozycję identyczną dla wszystkich warian-
tów, a następnie zamianę wybranych podsystemów na wersje odpowiadające poszczególnym
wariantom. W przykładzie z rysunku 13.13 oba warianty podsystemu NC1 ient korzystają ze
współdzielonych podsystemów Katalog części i I n t e r f e j s serwera, które jednak oferują
różne interfejsy dla każdego wariantu. W ten sposób uzyskujemy dekompozycję, w której każdy
z podsystemów jest bądź to niezależny od konkretnego wariantu (czyli obsługujący wszystkie
warianty), bądź specyficzny dla jednego lub kilku wariantów.
A konkretnie, wymienione problemy rozwiązywać można w sposób następujący.

• Jeden producent, wielu konsumentów. Jeśli proponowana zmiana jest typowa dla
konkretnego wariantu, nie powinna w żaden powodować modyfikacji współdzielo-
nych podsystemów; jeśli natomiast może być pożyteczna dla wszystkich wariantów,
powinna być w całości skoncentrowana we współdzielonych podsystemach. Rozwiąza-
nie przedmiotowego problemu jest więc kwestią starannego zarządzania zmianami.
• Długi cykl realizacji żądania zmiany może zostać wydatnie skrócony, jeśli zespołowi
proponującemu daną zmianę pozostawi się również kwestię jej walidacji. Zmiana
ta zostaje zaimplementowana jako nowa promocja współdzielonego podsystemu,
która zostaje przekazana wspomnianemu zespołowi w celu jej przetestowania; pozo-
stałe zespoły pracują tymczasem z dotychczasową wersją współdzielonego pod-
systemu. Z takim właśnie scenariuszem zetknęliśmy się już w sekcji 13.4.4, gdzie
zespół odpowiedzialny za wprowadzenie nawigowalnych map do systemu NC1 ient
modyfikował w związku z tym oprogramowanie serwera, podczas gdy zespół pod-
systemu EC1 ient korzystał z dotychczasowej, stabilnej wersji serwera. W sytuacji
gdy kilka zespołów zgłasza różne propozycje zmian we współdzielonym podsystemie,
zespół odpowiedzialny za ten podsystem organizuje realizację każdej z nich w ramach
odrębnej gałęzi, w celu wykluczenia ich wzajemnego wpływu na siebie do czasu, gdy
każda z nich stanie się względnie stabilna.
• Niespójności między platformami można minimalizować, dążąc do dekompozycji
niezależnej od konkretnego wariantu. Niskopoziomowe różnice między platforma-
mi rozwiązywane będą wówczas w podsystemach specyficznych dla poszczególnych
wariantów, być może za cenę dodatkowych obiektów pośredniczących lub redun-
dancji między podsystemami specyficznymi a współdzielonymi. Gdy jednak strategia
ta okaże się nieskuteczna (bo poszczególne warianty wyraźnie różnią się między so-
bą), być może zrównoleglenie projektów będzie rzeczywiście lepszą alternatywą.
626 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

Zarządzanie wieloma wariantami systemu jest, jak widać, trudnym zadaniem. Strategia
współdzielonych systemów, mimo iż wymaga dodatkowych inwestycji w fazie projektowania
systemu, daje w rezultacie wiele korzyści, z których najważniejsze to lepsza jakość i większa
stabilność współdzielonego kodu oraz lepsza spójność jakościowa między poszczególnymi
wariantami. Ponadto, gdy liczba wariantów jest dość duża, przewidywanie zawczasu proble-
mów specyficznych dla poszczególnych wariantów i planowanie w związku z nimi odpo-
wiedniego zarządzania konfiguracją pozwoli uzyskać znaczne oszczędności kosztów i czasu.

13.4.6. Zarządzanie propozycjami zmian i ich implementowaniem


Żądania zmian są motorem napędowym budowania nowych promocji i emisji: programiści
tworzą nowe promocje w związku poprawianiem wykrytych usterek i pojawianiem się no-
wych technologii, nowe wymagania klienta skutkują powstawaniem nowych emisji systemu.
Zakres żądanych zmian bywa bardzo zróżnicowany — od poprawienia „literówki" w opcji
menu do reimplementacji całego podsystemu w celu polepszenia jego wydajności. Praco-
chłonność konkretnej zmiany jest także uwarunkowana jej umiejscowieniem w czasie: żądanie
dodatkowej funkcjonalności w czasie przeglądu wymagań wiąże się co najwyżej z modyfi-
kacją szkicu dokumentu RAD, takie samo żądanie zgłoszone w trakcie testowania gotowej
implementacji wymaga już jednak zaawansowanej chirurgii całego systemu. Zakres i kontekst
proponowanych zmian stanowią domenę kolejnej aktywności zarządzania konfiguracją, jaką
jest zarządzanie zmianami.
Proces zarządzania zmianami może mieć różną postać, zależnie od stopnia złożoności
celów projektowych i stopnia formalizacji całego projektu. W przypadku skomplikowanych
systemów, od których wymaga się dużej niezawodności, żądanie zmian może mieć postać
wielostronicowego formularza [MIL Std. 480], wymagającego akceptacji wielu menedżerów,
która może być kwestią wielu tygodni. W przypadku prostego programu narzędziowego,
tworzonego przez jednego programistę, żądanie zmiany może być wyrażone w nieformalnej
rozmowie. W obu jednak przypadkach przetwarzanie żądania zmiany obejmuje wykonanie
następujących kroków, przedstawionych w formie diagramu aktywności na rysunku 13.14.

1. Zostaje zgłoszona propozycja zmiany, dotycząca wykrytej usterki lub nowej funkcjo-
nalności. Zgłaszającym może być każdy, w tym programista lub klient.
2. Propozycja zostaje oceniania pod kątem zgodności z celami projektowymi. W przy-
padku dużych projektów oceny takiej dokonuje dedykowany zespół, w mniejszym
projekcie — jego menedżer. Ocena taka może również obejmować analizę kosztów
i korzyści oraz wpływu proponowanej zmiany na resztę systemu.
3. Konsekwencją dokonanej oceny jest zaakceptowanie albo odrzucenie żądania zmiany.
4. Jeśli żądanie zmiany zostanie zaakceptowane, zostaje ona zaplanowana i zakwali-
fikowana pod względem priorytetu, po czym wyznaczeni programiści dokonują jej
implementacji.
5. Zaimplementowana zmiana poddana zostanie audytowi, przeprowadzanemu przez
zespół kontroli jakości lub osobę odpowiedzialną za zarządzanie emisjami.
13.5. Kierownicze aspekty zarządzania konfiguracją 627

Rysunek 13.14. Przykład procesu przetwarzania żądania zmiany

13.5. Kierownicze aspekty zarządzania konfiguracją


Z a j m i e m y się teraz z a r z ą d z a n i e m k o n f i g u r a c j ą w i d z i a n y m z p e r s p e k t y w y m e n e d ż e r a p r o j e k t u
— stojące przed n i m w związku z t y m zadania obejmują:

• D o k u m e n t o w a n i e z a r z ą d z a n i a k o n f i g u r a c j ą ( p a t r z sekcja 13.5.1),

• Przypisywanie odpowiedzialności związanych z zarządzaniem konfiguracją (patrz


sekcja 13.5.2),

• Planowanie aktywności związanych z zarządzaniem konfiguracj ą (patrz sekcj a 13.5.3),

• Z a r z ą d z a n i e i n t e g r a c j ą ciągłą ( p a t r z sekcja 13.5.4).

13.5.1. Dokumentowanie zarządzania konfiguracją


D o k u m e n t [IEEE Std. 828-2005] d e f i n i u j e s t a n d a r d y s p o r z ą d z a n i a P l a n u Zarządzania K o n -
figuracją Oprogramowania (SCMP — Software Configuration Management Plan). Plan taki
s t a n o w i d o k u m e n t a c j ę wszelkiej i n f o r m a c j i związanej z z a r z ą d z a n i e m k o n f i g u r a c j ą k o n k r e t -
n e g o p r o j e k t u ; s p o r z ą d z a n y jest w fazie p l a n o w a n i a p r o j e k t u i p o d d a n y p r o c e s o w i zarządzania
k o n f i g u r a c j ą , m o ż e b o w i e m z czasem ulegać z m i a n o m . Szczegółowość i objętość takiego p l a n u
zależna jest o d specyfiki o d n o ś n e g o p r o j e k t u : w p r z y p a d k u p r o j e k t u o k r y t y c z n y m z n a c z e n i u
m o ż e liczyć kilkadziesiąt s t r o n , w p r z y p a d k u k o n s t r u o w a n i a p r o t o t y p u k o n c e p c y j n e g o jest
zwykle kilkustronicowy. Treść p l a n u o b e j m u j e i n f o r m a c j ę sześciu rodzajów: wstęp, zarządzanie,
aktywności, h a r m o n o g r a m y , zasoby i konserwacja p l a n u — zgodnie z poniższym szablonem.
628 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

Plan Zarządzania Konfiguracją Oprogramowania


1. Wstęp
1.1. Cel
1.2. Zakres
1.3. Podstawowe terminy
1.4. Odwołania
2. Zarządzanie
2.1. Organizacja
2.2. Odpowiedzialności
3. Aktywności
4. H a r m o n o g r a m
5. Zasoby
6. Konserwacja planu

Wstęp d e f i n i u j e zakres t e m a t y c z n y i o d b i o r c ó w d o k u m e n t u , p o d s t a w o w ą t e r m i n o l o g i ę
i odwołania do innych dokumentów.
S e k c j a Zarządzanie opisuje organizację projektu, być m o ż e w formie odwołania do
Planu Zarządzania Projektem Programistycznym (patrz rozdział 14. „Zarządzanie projektem"),
i p o d z i a ł o d p o w i e d z i a l n o ś c i w zakresie z a r z ą d z a n i a k o n f i g u r a c j ą n a szczeblu o r g a n i z a c y j n y m
projektu.
W sekcji Aktywności o p i s a n e są s z c z e g ó ł o w o : i d e n t y f i k a c j a e l e m e n t ó w k o n f i g u r a c j i ,
p r o c e s k o n t r o l o w a n i a z m i a n , p r o c e s k r e o w a n i a emisji i ich a u d y t u o r a z p r o c e s k o n t r o l o w a n i a
s t a t u s u k o n f i g u r a c j i . O d p o w i e d z i a l n o ś ć za w y k o n y w a n i e w y m i e n i o n y c h a k t y w n o ś c i p r z y p i -
s a n a z o s t a ł a w sekcji Zarządzanie.
Sekcja Harmonogram p r e c y z u j e , k i e d y p o d e j m o w a n e m a j ą być w s p o m n i a n e a k t y w n o ś c i
i j a k b ę d ą k o o r d y n o w a n e . Szczegółowo z d e f i n i o w a n e są t u okoliczności u z a s a d n i a j ą c e ż ą d a n i e
z m i a n i ich weryfikacja w r a m a c h f o r m a l n e g o procesu.
Zasoby o b e j m u j ą narzędzia, techniki, sprzęt, p e r s o n e l i szkolenia n i e z b ę d n e d o w y k o -
nywania aktywności związanych z zarządzaniem konfiguracją.
P l a n z a r z ą d z a n i a k o n f i g u r a c j ą s a m jest d o k u m e n t e m p o d l e g a j ą c y m z m i a n o m i j a k o taki
s t a n o w i j e d e n z p o d m i o t ó w z a r z ą d z a n i a k o n f i g u r a c j ą . Z a s a d y jego u t r z y m y w a n i a i r e w i d o w a -
nia opisane są w sekcji Konserwacja planu, wymieniającej osoby odpowiedzialne za te czynności,
a także precyzującej częstotliwość jego uaktualniania i f o r m a l n y proces w p r o w a d z a n i a d o
niego zmian.
S t a n d a r d [IEEE Std. 828-2005] z d e f i n i o w a n y został z i n t e n c j ą jego p r z y d a t n o ś c i d o p r o -
jektów dowolnego typu — zarówno projektów o krytycznym znaczeniu, jak i drobnych dar-
mowych narzędzi.

13.5.2. Przypisywanie odpowiedzialności


Z a r z ą d z a n i e k o n f i g u r a c j ą o b e j m u j e wiele r ó ż n y c h z a d a ń w y k o n y w a n y c h p r z e z w i e l u uczest-
n i k ó w p r o j e k t u . P o d o b n i e j a k p o d c z a s p r o j e k t o w a n i a systemu, z a d a n i a w y m a g a j ą c e z a c h o -
w y w a n i a s p ó j n o ś c i p o w i n n y być w y k o n y w a n e p r z e z niewielką liczbę o s ó b — i t a k jak w fazie
p r o j e k t o w a n i a s y s t e m u zespół kilku a r c h i t e k t ó w d e c y d u j e o kształcie d e k o m p o z y c j i s y s t e m u ,
13.5. Kierownicze aspekty zarządzania konfiguracją 629

tak w związku z zarządzaniem konfiguracją nieliczny o s o b o w o zespół d o k o n u j e identyfikacji


elementów konfiguracji i agregatów CM. Ponadto, podobnie jak przy testowaniu, kontrola
jakości p r o m o c j i i emisji p o w i n n a być w y k o n y w a n a przez osoby niezaangażowane w ich two-
rzenie i i m p l e m e n t o w a n i e .
Z a r z ą d z a n i e k o n f i g u r a c j ą w i ą ż e się z w y z n a c z e n i e m p r z e z m e n e d ż e r a p r o j e k t u n a s t ę -
p u j ą c y c h ról.

• M e n e d ż e r k o n f i g u r a c j i t o o d p o w i e d z i a l n o ś ć za i d e n t y f i k a c j ę e l e m e n t ó w k o n f i g u r a c j i
i n i e k i e d y za d e f i n i o w a n i e p r o c e d u r t w o r z e n i a p r o m o c j i i e m i s j i . R o l a t a jest c z ę s t o
ł ą c z o n a z r o l ą a r c h i t e k t a s y s t e m u ( p a t r z s e k c j a 7.5.2).

• K o n t r o l e r o d p o w i e d z i a l n y j e s t za a k c e p t o w a n i e a l b o o d r z u c a n i e p r o p o n o w a n y c h
z m i a n n a p o d s t a w i e i c h relacji d o c e l ó w p r o j e k t o w y c h . Z a l e ż n i e o d k o m p l i k a c j i sa-
m e g o procesu d o k o n y w a n i a zmian, rola ta m o ż e o b e j m o w a ć także ocenianie d o k o -
n a n y c h z m i a n i p l a n o w a n i e n a s t ę p n y c h . R o l ę t ę c z ę s t o ł ą c z y się z r o l ą k i e r o w n i k a
zespołu lub menedżera projektu.

• Programista, p o z a „ n o r m a l n y m " r e a l i z o w a n i e m p r o j e k t u , t w o r z y p r o m o c j e o d z w i e r -
ciedlające p r o p o n o w a n e z m i a n y . P o d s t a w o w y m z a d a n i e m p r o g r a m i s t y w a s p e k c i e
z a r z ą d z a n i a k o n f i g u r a c j ą jest a k t u a l i z o w a n i e w e r s j i o p r o g r a m o w a n i a i r o z w i ą z y w a n i e
konfliktów występujących przy scalaniu wersji.

• A u d y t o r o d p o w i a d a za w y b ó r i e w a l u a c j ę p r o m o c j i a w a n s o w a n y c h d o r a n g i e m i s j i ,
a także za s p ó j n o ś ć i k o m p l e t n o ś ć tych emisji. Rolę tę w y k o n u j e zazwyczaj zespół
kontroli jakości.

Role z w i ą z a n e z z a r z ą d z a n i e m k o n f i g u r a c j ą d e f i n i o w a n e są w fazie p l a n o w a n i a p r o j e k t u
i p r z y d z i e l a n e s t o s u n k o w o w c z e ś n i e dla z a p e w n i a n i a s p ó j n o ś c i . Z b y t częste z m i a n y w p r z y -
dziale t y c h r ó l m o g ą p o w a ż n i e o g r a n i c z y ć k o r z y ś c i p ł y n ą c e z z a r z ą d z a n i a k o n f i g u r a c j ą i o d b i ć
się n e g a t y w n i e n a p r o c e s i e k o n t r o l o w a n i a z m i a n .

13.5.3. Planowanie aktywności w ramach zarządzania konfiguracją


M e n e d ż e r o w i e p r o j e k t ó w p o w i n n i p l a n o w a ć z a r z ą d z a n i e i c h k o n f i g u r a c j ą w fazie s t a r t o w e j .
W i ę k s z o ś ć p r o c e d u r z a r z ą d z a n i a k o n f i g u r a c j ą m o ż e b y ć z d e f i n i o w a n a jeszcze p r z e d r o z p o -
c z ę c i e m realizacji p r o j e k t u , p r o c e d u r y t e są b o w i e m n i e z a l e ż n e o d s a m e g o s y s t e m u , a r a c z e j
o d c e l ó w p r o j e k t o w y c h i w y m a g a ń p o z a f u n k c y j n y c h , s z c z e g ó l n i e t y c h , k t ó r e z w i ą z a n e są
z bezpieczeństwem i niezawodnością.
Oto kluczowe elementy planowania zarządzania konfiguracją.

• Zdefiniowanie procesów zarządzania konfiguracją.

• Z d e f i n i o w a n i e i p r z y p i s a n i e ról.

• Z d e f i n i o w a n i e k r y t e r i ó w z m i a n , czyli określenie, k t ó r e a t r y b u t y p r o d u k t ó w m o g ą b y ć
z m i e n i a n e i w j a k z a a w a n s o w a n e j fazie p r o j e k t u i c h z m i a n y m o g ą b y ć d o k o n y w a n e .

• Zdefiniowanie kryteriów, jakie spełniać m u s z ą p r o m o c j e a w a n s o w a n e n a emisje.


630 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

C o więcej, p r o c e d u r y i n a r z ę d z i a z w i ą z a n e z z a r z ą d z a n i e m k o n f i g u r a c j ą p o w i n n y stać
się d o s t ę p n e , z a n i m j e s z c z e p o j a w i się k o n i e c z n o ś ć d o k o n a n i a p i e r w s z e j z m i a n y — t a k b y
wszystkie z m i a n y m o g ł y być rejestrowane, o c e n i a n e i śledzone.

13.5.4. Integracja ciągła jako optymalizacja zarządzania promocjami


i ich testowaniem
O b i e k t o w o z o r i e n t o w a n e t w o r z e n i e s y s t e m u i n f o r m a t y c z n e g o jest p r o c e s e m i t e r a t y w n y m ,
c e c h u j ą c y m się p r z y r o s t o w y m d o d a w a n i e m n o w y c h c e c h o r a z u n o w o c z e ś n i a n i e m i s t n i e j ą -
cych p r o d u k t ó w . W kategoriach zarządzania konfiguracją każdy programista k o n t y n u u j e
p r a c ę w r a m a c h s w e j w ł a s n e j gałęzi, t e s t u j ą c l o k a l n i e n o w o z a i m p l e m e n t o w a n e c e c h y i n a -
stępnie scalając w p r o w a d z a n e z m i a n y z g ł ó w n y m p n i e m . Każde takie scalenie oznacza po-
n o w n e integrowanie zrewidowanego systemu, wymagające wykonania testów integracyjnych
i t e s t ó w r e g r e s y j n y c h . W m i a r ę j a k z w i ę k s z a się liczba i m p l e m e n t o w a n y c h r ó w n o l e g l e cech,
i c h i n t e g r o w a n i e z g ł ó w n y m p n i e m z a c z y n a s t a w a ć się w ą s k i m g a r d ł e m — p o n i e w a ż k a ż d y
akt integrowania w y m a g a wyłącznego d o s t ę p u d o głównego pnia, programiści coraz dłużej
o c z e k u j ą n a m o ż l i w o ś ć z i n t e g r o w a n i a „ s w o i c h " z m i a n . W efekcie k o n f l i k t y m i ę d z y z m i a n a m i
w p r o w a d z a n y m i p r z e z r ó ż n y c h p r o g r a m i s t ó w w y k r y w a n e są s t o s u n k o w o p ó ź n o , c o p o w o d u j e
d o d a t k o w e wydłużenie w s p o m n i a n e g o czasu oczekiwania.
Integracja ciągła (continuus integration), opisana przez P. Duvala, S. Matyasa i A. Glovera
[Duval i in., 2007], t o t e c h n i k a łącząca z a r z ą d z a n i e p r o m o c j a m i ( p a t r z sekcja 13.4.2) i t e s t o w a n i e
i n t e g r a c y j n e ( p a t r z sekcja 11.4.4) z z a m i a r e m j a k n a j w c z e ś n i e j s z e g o w y k r y w a n i a p r o b l e m ó w
i n t e g r a c y j n y c h i w k o n s e k w e n c j i r e d u k o w a n i a o p i s a n e g o s y m p t o m u w ą s k i e g o g a r d ł a integracji.
P o d s t a w o w y m celem integracji ciągłej jest j e d n a k z a c h o w a n i e stabilności g ł ó w n e g o p n i a ,
s t a n o w i ą c e g o w i a r y g o d n y p u n k t o d n i e s i e n i a dla w s z y s t k i c h p r o g r a m i s t ó w . I n t e g r a c j a ciągła
c h a r a k t e r y z u j e się n a s t ę p u j ą c y m i c e c h a m i .

• Pojedyncza, publiczna gałąź integracyjna. Programiści implementują i testują zmiany


lokalnie, w r a m a c h s w y c h p r y w a t n y c h gałęzi, lecz t w o r z o n e p r z e z n i c h p r o m o c j e inte-
g r o w a n e są z p o j e d y n c z y m g ł ó w n y m p n i e m w s p ó ł d z i e l o n y m p r z e z wszystkich p r o g r a -
m i s t ó w . N i e m a o d d z i e l n y c h gałęzi w y m a g a j ą c y c h późniejszego k o s z t o w n e g o scalania.

• Automatyczne generowanie binariów i testowanie regresyjne. Każde zintegrowanie


z m i a n y z g ł ó w n y m p n i e m (czyli k a ż d e u t w o r z e n i e n o w e j p r o m o c j i ) a u t o m a t y c z n i e
w y z w a l a w y k o n a n i e n i e z b ę d n y c h t e s t ó w r e g r e s y j n y c h . Jeśli p r ó b a g e n e r o w a n i a b i -
n a r i ó w z a k o ń c z y się n i e p o m y ś l n i e , z a i n t e r e s o w a n y p r o g r a m i s t a jest o t y m n a t y c h -
m i a s t i n f o r m o w a n y , c o u m o ż l i w i a szybkie p o p r a w i e n i e usterki albo w y c o f a n i e z m i a n y
z g ł ó w n e g o p n i a . Jeśli n a t o m i a s t z a ł a m i ą się t e s t y r e g r e s y j n e i z l o k a l i z o w a n a z o s t a n i e
przyczyna tego załamania, p r o g r a m i s t a autor p r o m o c j i m o ż e spokojnie u s u w a ć tę
przyczynę, podczas gdy inni m o g ą kontynuować swoje integrowania.

• Częste generowanie promocji. P r o g r a m i ś c i p o w i n n i i n t e g r o w a ć „swoje" z m i a n y z głów-


n y m p n i e m d o s t a t e c z n i e często, z w y k l e c o n a j m n i e j r a z d z i e n n i e . W p i e r w s z e j k o -
l e j n o ś c i i n t e g r o w a n e są z m i a n y w y n i k a j ą c e z r e f a k t o r y z a c j i w p ł y w a j ą c e j n a a r c h i t e k -
turę systemu, a następnie efekty dodawania nowej funkcjonalności. G w a r a n t u j e to
wykrywanie i rozwiązywanie zależności m i ę d z y z m i a n a m i , z a n i m n o w e elementy
funkcjonalności zostaną z a i m p l e m e n t o w a n e na bazie kiepskiego projektu.
13.5. Kierownicze aspekty zarządzania konfiguracją 631

• Transparentny status generacji. Status generowania binariów przez poszczególnych


programistów jest widoczny dla wszystkich programistów, co jednej strony, stanowi
presję na jak najszybsze usuwanie usterek, z drugiej natomiast, powstrzymuje pro-
gramistów od integrowania zmian nieprzetestowanych pod kątem poprawnej kom-
pilacji i konsolidacji.

Na rysunku 13.15 widoczny jest ekran jednego z narzędzi wspomagających integrację


ciągłą — CruiseControl [CruiseControl]. Każdy kwadratowy przycisk na „tablicy rozdzielczej"
(idashboard) reprezentuje jeden podsystem w kontekście związanych z nim testów. Pomyślne
generowanie binariów oznaczane jest „ptaszkiem", generowanie nieudane sygnalizowane jest
znakiem wykrzyknika. Dodatkowo wyróżniane są generowania, w których ostatnio usunięto
przyczyny usterek. Kliknięcie przycisku spowoduje udostępnienie szczegółów danego gene-
rowania, na przykład wykazu niezaliczonych testów oraz ostatnio dodanych poprawek będą-
cych przyczyną ich załamania. Rzut oka na wspomnianą „tablicę rozdzielczą" pozwala na szybką
ocenę stopnia względnej dojrzałości poszczególnych podsystemów.

Rysunek 13.15. CruiseControl — narzędzie wspomagające integrację ciągłą. Każdy kwadrat repre-
zentuje podsystem (w kategoriach CruiseControl nazywany „projektem")

Główną korzyścią wynikającą z integrowania ciągłego jest wczesne wykrywanie nowych


problemów z integracją zmian. Do tego jednak konieczne jest utrzymywanie generowania
i testowania w niewielkich ryzach czasowych, nawet jeśli zwiększy się rozmiar i stopień kom-
plikacji projektu. Ten cel osiąga się za pomocą techniki budowania wieloetapowego (staged
build), polegającej na podziale testowania na kilka faz. W pierwszej fazie generowane są bina-
ria i wykonywany minimalny zestaw testów regresyjnych (faza ta nazywana jest popularnie
632 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

„testem na dym" — smoke test)9, mający na celu wykrycie naj poważniej szych problemów. Po
pomyślnym przejściu przez tę fazę wykonywanie kolejnych testów (dotyczących między in-
nymi wydajności i zachowania systemu w obliczu realistycznych danych) nie blokuje już głów-
nego pnia, który staje się dostępny do integrowania dla innych poprawek. Podział poszcze-
gólnych testów na obie fazy jest więc kwestią kompromisu między wczesnym wykrywaniem
problemów a wydajnością integrowania.
Integracja ciągła sprzyja lepszemu wykorzystywaniu technik opisywanych w tym roz-
dziale i w rozdziale 11. „Testowanie", różni się jednak od nich uwydatnieniem poszczególnych
elementów i skalą czasową. Oznacza bowiem częste integrowanie drobnych zmian, w przeci-
wieństwie do integrowania wstępującego i integrowania zstępującego, które polegają na „hur-
towym" integrowaniu całych podsystemów, w stosunkowo dużych odstępach czasu. W konse-
kwencji integracja ciągła spisuje się bardzo dobrze w odniesieniu do projektów o ewoluującej
architekturze, realizowanych przez pojedynczy zespół programistów, podczas gdy integro-
wanie na poziomie podsystemów przydatne jest dla projektów o dobrze zdefiniowanej archi-
tekturze i podsystemach opracowywanych równolegle przez wiele zespołów.

13.6. Literatura uzupełniająca


Historycznie zarządzanie konfiguracją pojawiło się jako dyscyplina inżynierii oprogramowa-
nia w tym samym czasie, gdy powszechnie zaczęto mówić o „kryzysie oprogramowania".
Większość prac z zakresu zarządzania konfiguracją motywowana była dużymi, kontraktowa-
nymi projektami, w których zmiany w wymaganiach i efektywne zarządzanie emisjami stały
się czynnikiem krytycznym. Model Capability Maturity Model SEI klasyfikuje na najniższym
poziomie dojrzałości te organizacje projektu, które nie obejmują zarządzania konfiguracją
(patrz sekcja 15.3). Mimo coraz większego znaczenia zarządzania konfiguracją w warunkach
systemów wielowariantowych i systemów tworzonych w rozproszeniu, dyscyplina ta wciąż
stwarza wrażenie niedopracowanej. Książka W. A. Babicha [Babich, 1986], mimo że nie pierw-
szej już młodości, wciąż pozostaje standardowym podręcznikiem z tej dziedziny.
Pierwsze narzędzia kontroli wersji pojawiły się w latach 70. ubiegłego wieku, gdy roz-
powszechniło się użycie pamięci dyskowych i taśm magnetycznych. Mechanizmy kontroli wer-
sji i zarządzania generowaniem wbudowane są w niektóre systemy operacyjne, na przykład
w systemie VMS dla komputerów VAX każda zmiana wprowadzona do pliku powoduje utwo-
rzenie jego nowej wersji, zaś użytkownik ma możliwość łatwego usuwania wersji niepotrzeb-
nych; z kolei w świecie Uniksa podsystem SCCS {Source Code Control System), rozprowadzany
jako część systemu operacyjnego umożliwia programiście przyrostowe zapamiętywanie kolej-
nych wersji tego samego pliku (wraz z gałęziami) pod wspólną nazwą, co opisał M. J. Rochkind
[Rochkind, 1975]. W połowie lat 80. ub. stulecia SCCS wyparty został przez swego następcę

9
Określenie to zaczerpnięte jest z elektroniki: objawem podłączenia zasilania do wadliwie wykonanego
obwodu może być spalenie się układu scalonego, dające w efekcie błękitny dymek. Gdy po podłącze-
niu zasilania nic się nie dymi, obwód gotowy jest do dalszych testów. Czytelnikom zainteresowanym
metafizycznym znaczeniem owego błękitnego dymku (elektronicy widzą w nim wręcz duszę układu
scalonego) oraz ogólnie kolorytem żargonowych (i nie tylko) określeń powstałych w ciągu ostatniego
półwiecza na kanwie technologii komputerowych chciałbym polecić książkę Leksykon hackingu, wyd.
Helion 2003, http://helion.pl/ksiazki/hacklk.htm — przyp. tłum.
13.7. Ćwiczenia 633

— RCS (Revision Control System) — wprowadzającego możliwość pracy wielu użytkowników


i dostarczającego mechanizmy blokad i koordynacji, o czym pisze W. Tichy [Tichy, 1985].
Kilka lat później pojawił się CVS (Concurrent Version System), o którym napisał B. Berliner
[Berliner, 1990], umożliwiający programistom równoległe modyfikowanie tych samych plików
i dostarczający środki do scalania gałęzi oraz konfliktów wynikających przy okazji tego scalania.
Narzędzie o nazwie Subversion, które pojawiło się w roku 2000, oferuje interfejs podobny jak
CVS, jednak dodatkowo zawiera historię zapisu wersji dla kompletnych katalogów, śledzenie
zmiany nazw plików, ich przemieszczania między katalogami oraz usuwania. Historia zmian
zapisywana jest w bazie danych, a integrowanie poszczególnych zmian odbywa się w sposób
transakcyjny, gwarantujący niepodzielność integracji. Począwszy od końcowych lat ubiegłego
stulecia, wiele komercyjnych narzędzi zarządzania konfiguracją integruje wsparcie dla proce-
su żądania zmian, ich śledzenia i powiadamiania o nich.
Integracja ciągła stanowi jeden z fundamentów programowania ekstremalnego (Extreme
Programming — opisanego przez K. Becka i C. Andresa [Beck i Andres, 2005]) i jest szczegól-
nie popularna w kręgu użytkowników języka Java, za sprawą CruiseControl [CruiseControl]
— darmowego narzędzia automatyzującego generowanie binariów i testowanie regresyjne.
Inny przykładami popularnych narzędzi wspomagających ciągłą integrację są Ti nderbox
i Bonsai, opracowane jako części projektu Mozilla, będącego oprogramowaniem open source.

13.7. Ćwiczenia
13.1. System RCS wykorzystuje podejście zwane odwrotną deltą, polegające na prze-
chowywaniu kompletnej postaci najnowszej wersji pliku oraz ciągu różnic między
sąsiadującymi niższymi wersjami; gdy przykładowo dla danego pliku istnieją wersje
1.1, 1.2 i 1.3, RCS zapamiętuje explicite wersję 1.3, różnice między wersjami 1.2 i 1.3
oraz różnice między wersjami 1.1 i 1.2. Gdy pojawi się nowa wersja 1.4, zostanie za-
pamiętana explicite, a dotychczas przechowywana wersja 1.3 zostanie zastąpiona
zbiorem różnic między wersjami 1.3 i 1.4. Wyjaśnij, dlaczego to podejście jest bar-
dziej rozsądne od podejścia zwanego prostą deltą, zgodnie z którym jawnie zapa-
miętywana jest wersja początkowa (o najniższym numerze), a następne wersje re-
prezentowane są za pomocą różnic w stosunku do wersji poprzedniej.
13.2. System CVS wykorzystuje reguły tekstowe przy wykrywaniu konfliktów scalania:
plik traktowany jest jako ciąg wierszy tekstu, a za kolidujące uważane są zmiany od-
noszące się do tego samego wiersza. Jeżeli w jednej z wersji wiersz taki nie istnieje,
CVS traktuje to jako brak konfliktu i automatycznie dokonuje scalenia. Załóżmy na
przykład, że plik zawiera definicję klasy z trzema metodami a ( ) , b ( ) i c(), i dwóch
programistów niezależnie od siebie modyfikuje ten plik. Gdy dokonane przez nich
zmiany (wyrażające się zestawem zmodyfikowanych wierszy) nie są rozłączne w mo-
mencie scalania, CVS sygnalizuje konflikt. Wyjaśnij na stosownym przykładzie,
dlaczego takie podejście nie jest w stanie wykryć niektórych konfliktów przy scalaniu.
13.3. Systemy zarządzania konfiguracją, takie jak RCS, CVS i Perforce wykorzystują kom-
pletną ścieżkę pliku do jego identyfikowania jako elementu konfiguracji. Wyjaśnij,
w jaki sposób cecha ta przeszkadza w zarządzaniu konfiguracją agregatów CM, nawet
mimo obecności etykiet.
634 Rozdział 12. • Zarządzanie r a c j o n a l i z a c j ą

13.4. W y j a ś n i j , jakie korzyści m o ż e dać p r o g r a m i s t o m z a r z ą d z a n i e k o n f i g u r a c j ą n a w e t


w w a r u n k a c h b r a k u a u d y t u i k o n t r o l i z m i a n . Z i l u s t r u j swe w y j a ś n i e n i e d w o m a
przykładowymi scenariuszami.

13.5. W rozdziale 12. „Zarządzanie racjonalizacją" pokazaliśmy, jak i n f o r m a c j a związana


z racjonalizacją m o ż e być reprezentowana w m o d e l u zagadnień. Narysuj diagram klas
U M L dla systemu śledzenia p r o b l e m ó w , wykorzystującego m o d e l zagadnień d o opi-
sywania i d y s k u t o w a n i a z m i a n oraz u w i d a c z n i a n i a ich p o w i ą z a ń z poszczególnymi
wersjami. S k o n c e n t r u j się w y ł ą c z n i e n a o b i e k t a c h d z i e d z i n o w y c h s y s t e m u .

13.6. W rozdziale 11. „Testowanie" opisywaliśmy sposób znajdowania przez zespół kontroli
jakości usterek w p r o m o c j a c h t w o r z o n y c h przez zespoły p o d s y s t e m ó w . N a r y s u j dia-
g r a m aktywności U M L o b e j m u j ą c y a k t y w n o ś c i z w i ą z a n e z p r z e t w a r z a n i e m z m i a n
i t e s t o w a n i e m w w a r u n k a c h p r o j e k t u r e a l i z o w a n e g o p r z e z wiele zespołów.

Bibliografia
W. A. Babich Software Configuration Management, Addison-Wesley, Reading,
[Babich, 1986]
MA, 1986,
K. Beck, C. Andres Extreme Programming Explained: Embrace Change,
[Beck i Andres, 2005]
wyd. drugie, Addison-Wesley, Reading, MA, 2005.
B. Berliner „CVS II: Parallelizing software development", Proceedings of the
[Berliner, 1990]
1990 USENIX Conference, Washington, DC, str. 22 - 26, styczeń 1990.
E. H. Bersoff, V. D. Henderson, S. G. Siegel, Software Configuration Management:
[Bersoffiin., 1980]
An Investment in Product Integrity, Prentice Hall, Englewood Cliffs, NJ, 1980.
[CruiseControl] h ttp:// cruisecontrol. sourceforge. net/.
[Dart, 1991] S. Dart „Concepts in configuration m a n a g e m e n t systems", Third
International Software Configuration Management Workshop, ACM,
czerwiec 1991.
[Duval i in., 2007] P. Duval, S. Matyas, A. Glover Continuous Integration: Improving Software
Quality and Reducing Risk, Addison-Wesley, Reading, MA, 2007.
[IEEE Std. 828-2005] IEEE Standard for Software Configuration Management Plans, IEEE
Standards Board, sierpień 2005.
[IEEE Std. 1042- IEEE Guide to Software Configuration Management, IEEE Standards Board,
1987] wrzesień 1987.
[Kemerer, 1997] C. F. Kemerer „Case 7: Microsoft Corporation: Office Business Unit", Software
Project Management: Readings and Cases, Irwin/McGraw-Hill, Boston,
MA, 1997.
[Knuth, 1986] D. E. Knuth The TeXbook, Addison-Wesley, Reading, MA, 1986.
[Lebłang, 1994] D. Leblang „The CM challenge: Configuration management that works",
w: W. F. Tichy (red.), Configuration Management, t. 2 of Trends in Software,
Wiley, New York, 1994.
[MIL Std. 480] MIL Std. 480, U.S. Department of Defense, Washington, DC.
[Moziłła] http://www. mozilla. org/
[Perforce] http://www.perforce, com/
[POSIX, 1990] Portable Operating System Interface for Computing Environments, w: IEEE
Std. 1003.1,1990.
Bibliografia 635

[Raymond, 1998] E. R a y m o n d The cathedral and the bazaar, dostępne p o d adresem


http://www.tuxedo.org/-esr/writings/cathedral-bazaar/cathedral-bazaar.html,
1998.
[Rochkind, 1975] M. J. Rochkind The Source Code Control System, „IEEE Transactions
on Software Engineering", SE-1(4), str. 255 - 265,1975.
[Steelman, 1978] Requirements for high order computer programming languages: Steelman,
U.S. Department of Defense, Washington, DC, 1978.
[Tichy, 1985] W. Tichy RCS — A system for version control, „Software Practice
and Experience", t. 15, nr 7, 1985
14.1. Wstęp: uruchomienie misji STS-51L 638

14.2. O zarządzaniu projektem ogólnie 639

14.3. Koncepcje zarządzania projektem 646


14.3.1. Zadania i aktywności 646
14.3.2. Produkty, pakiety pracy i role 646
14.3.3. Struktura podziału pracy 648
14.3.4. Model zadań 649
14.3.5. Macierz kwalifikacji 650
14.3.6. Plan zarządzania projektem 651
14.4. Aktywności klasycznego zarządzania projektem 653
14.4.1. Planowanie projektu 654
14.4.2. Organizowanie projektu 659
14.4.3. Kontrolowanie projektu 665
14.4.4. Kończenie projektu 671

14.5. Aktywności „zwinnej" realizacji projektu 673


14.5.1. Planowanie projektu: wykazy zaległości produktu i przebiegu 674
14.5.2. Organizowanie projektu 675
14.5.3. Kontrolowanie projektu: dni robocze i wykresy wygaszania - 675
14.5.4. Kończenie projektu: przeglądy przebiegów 677

14.6. Literatura uzupełniająca 677

14.7. Ćwiczenia 679

Bibliografia 680
Zarządzanie projektem

Przestań upierać się jak inżynier i pomyśl wreszcie jak menedżer.


— zdanie wypowiedziane na zebraniu, na którym zadecydowano
o uruchomieniu misji 51L Challengera

J \ ^ e n e d ż e r projektu nie generuje bezpośrednio, sam z siebie, użytecznych produktów: on


raczej zajmuje się zapewnieniem i koordynowaniem zasobów niezbędnych do tego, by inni
uczestnicy projektu mogli takie produkty generować. Zarządzanie projektem wymaga umiejęt-
ności kierowniczych i społecznych, niezbędnych do tego, by przewidywać potencjalne proble-
my i implementować ich skuteczne rozwiązania. Właśnie ów brak bezpośrednio namacalnych
efektów działań menedżerów, a także pozatechnologiczny (w większości) grunt owych działań
sprawiają, że stają się oni ulubionymi obiektami dowcipów opowiadanych przez programistów.
Niezależnie jednak od autoramentu i poziomu merytorycznego owych żartów, funkcja mene-
dżera ma znaczenie krytyczne dla doprowadzenia projektu do pomyślnego końca, choćby
w kontekście niesamowitej złożoności realizowanych obecnie systemów, na którą nakłada się
fatum dynamicznych zmian, wymuszanych nieprzewidywalnymi w większości okoliczno-
ściami. Menedżerowie nie podejmują raczej decyzji o charakterze technologicznym — często
nawet nie mają odpowiednich kwalifikacji do ich trafnego podejmowania — są natomiast
odpowiedzialni za koordynowanie działań w ramach projektu w taki sposób, by wysokiej
jakości system zrealizowany został w założonym terminie i przy nieprzekroczeniu dostępnego
budżetu. Podstawowymi narzędziami pracy menedżera są: planowanie, monitorowanie,
kontrolowanie, zarządzanie ryzykiem i sytuacjami awaryjnymi. W tym rozdziale opiszemy
zarządzanie projektem z punktu widzenia jego menedżera. Założymy przy tym — typową
dla większości współczesnych projektów — dwustopniową hierarchię organizacyjną. Opi-
szemy zarówno zarządzanie projektami w stylu klasycznym, czyli w oparciu o struktury po-
działu pracy, planowanie zadań, przydzielanie ról, zarządzanie ryzykiem, plany menedżerskie
oraz generalnie ustalony a priori zbiór wymagań i „kamieni milowych", jak i w oparciu
o „zwinne" (agile) metody, zmierzające do dostarczania produktu w sposób przyrostowy, być
może w połączeniu z renegocjowaniem stawianych mu wymagań w miarę postępu realizacji.
W rozdziale następnym skupimy naszą uwagę na modelach nazywanych ogólnie „cyklami
życiowymi oprogramowania" (software life cycles), których celem jest wielokrotne wykorzy-
stywanie i doskonalenie wiedzy nabywanej w związku z zarządzaniem kolejnymi projektami.
W obu rozdziałach — tym i następnym — prezentować będziemy przy tym optymistycz-
ne, akademickie ujęcie zarządzania projektami; w rozdziale 16. („Wszystko razem, czyli

1
Patrz http://www.nasaspaceflight.com/2007/01/remembering-the-mistakes-of-challenger/ — przyp. tłum.
638 Rozdział 12. • Zarządzanie racjonalizacją

metodologie") jednak zastanowimy się, co dzieje się, gdy rzeczywistość wymyka się z pod-
ręcznikowych ram i przyjrzymy się niektórym metodologiom i heurystykom umożliwiają-
cym radzenie sobie z zaskakującymi nieraz sytuacjami.

14.1. Wstęp: uruchomienie misji STS-51L


Na początek jednak przypomnijmy tragedię, która ćwierć wieku temu rozegrała się na
oczach całego świata.

STS-51L — katastrofa C h a l l e n g e s
28 stycznia 1986 roku prom kosmiczny Challenger realizujący misję STS-51L eksplodował 73 sekundy
po starcie, w wyniku czego zginęła cała siedmioosobowa załoga. Prezydencka komisja Rogersa
powołana do zbadania przyczyny wypadku ustaliła, że zapłon gazów wydostających się przez nie-
szczelne złącze w prawej rakiecie pomocniczej na paliwo stałe spowodowało wybuch w o d o r u
w zewnętrznym zbiorniku. Zgodnie z konkluzją komisji, duży przyczynek do tragedii miały poważ-
ne defekty w zakresie procesów decyzyjnych w NASA.

Główne komponenty promu kosmicznego to orbiter, rakiety pomocnicze na paliwo stałe i zewnętrzny
zbiornik paliwa. Orbiter jest pomieszczeniem dla załogi i ekwipunku, a rakiety pomocnicze spełniają
większość funkcji związanych z wyniesieniem go na orbitę. Gdy wyczerpią zapas paliwa, stają się zbędne,
zostają odrzucone i spadają do oceanu. Zewnętrzny zbiornik, dostarczający paliwa dla maszynerii or-
bitera, także jest odrzucany przed wejściem na orbitę. Rakiety pomocnicze zbudowane zostały
przez prywatnego podwykonawcę, firmę Morton Thiokol. By możliwy był ich transport, zostały
dostarczone w częściach i poskładane na stanowisku startowym. Poszczególne sekcje połączone
były za pomocą złączy; właśnie nieszczelność tych złączy spowodowała wspomniany wyciek gazów.

Jak ustaliła komisja Rogersa, inżynierowie z firmy Thiokol świadomi byli zagrożenia spowodo-
wanego takim obrotem sprawy: analiza rakiet pomocniczych odrzucanych przez wahadłowiec
w poprzednich misjach wykazała erozję uszczelek, zwanych popularnie „O-ringami", spowodowaną
wyciekającymi gazami i szczególnie groźną w sytuacji, gdy start odbywa się przy niskiej temperaturze
otoczenia. Fakt ten zaalarmował inżynierów choćby z tego względu, że projekt O-ringów nie
przewidywał ich odporności na taki kontakt. Menedżerowie zaliczyli ten problem w poczet „akcep-
towalnego ryzyka", jako że w poprzednich misjach erozja O-ringów nie miała katastrofalnych na-
stępstw. Inżynierowie w Thiokol bezskutecznie usiłowali przekonać swych przełożonych o powadze
problemu. W dniach poprzedzających start wahadłowca inżynierowie ci zaniepokoili się niską tem-
peraturą na stanowisku startowym i próbowali przekonać swych menedżerów do zaniechania
startu. Firma Thiokol, początkowo optująca za odłożeniem startu, ostatecznie zmieniła swe oficjalne
stanowisko, uginając się pod presją NASA.

Zarówno NASA, jak i Thiokol zarządzane były zgodnie z hierarchiczną strukturą, typową dla instytucji
administracji publicznej. W ramach tej struktury inżynierowie kontaktują się bezpośrednio z mene-
dżerami swych grup, ci z kolei raportują problem menedżerowi projektu i tak dalej. Każdy szczebel

2
Prezentowany tutaj opis tragedii Challengera ma charakter wybiórczy i służy jedynie jako (spekta-
kularny) przykład błędów w zarządzaniu projektem; zarówno techniczne, jak organizacyjne błędy
p r o w a d z ą c e w rezultacie do katastrofy były bardziej skomplikowane, niż m o g ł o b y to wynikać
z przedstawionego tutaj streszczenia. Czytelników zainteresowanych szczegółami ustaleń komisji
Rogersa odsyłamy do jej raportu [Rogers i in., 1986] oraz do książek poświęconych katastrofie, między
innymi do książki D. Yaughana [Yaughan, 1996].
14.2. O zarządzaniu projektem ogólnie 639

wymiany informacji kryje niebezpieczeństwo zmodyfikowania, zniekształcenia i utraty części


informacji przed przekazaniem jej do wyższego szczebla organizacji, zależnie od szczegółów
zainteresowania poszczególnych szczebli. Misja STS-51L była już wielokrotnie odkładana, jej opóź-
nienie wynosiło już kilka miesięcy i menedżerowie obu firm chcieli za wszelką cenę dotrzymać kolej-
nego terminu; zaważyło to zarówno na prawidłowości przekazywania informacji, jak i adekwatności
oceny ryzyka w obu firmach.

Ostatecznie więc, mimo iż bezpośrednią przyczyną katastrofy okazał się błąd projektowy złącza rakiety
pomocniczej, to zdaniem komisji Rogersa miało na nią wpływ także wadliwe funkcjonowanie struk-
tury komunikacyjnej w Thiokol i NASA, prowadzące do nieprawidłowej oceny ryzyka i w efekcie
do startu niesprawnego wahadłowca.

Zarządzanie projektem ma na celu dostarczenie wysokiej jakości systemu, zgodnie


z harmonogramem i budżetem — zwróćmy uwagę na podstawowe elementy kryjące się za
tym stwierdzeniem: jakość, czas i pieniądze. By zapewnić zrealizowanie systemu w ramach
dostępnego budżetu, menedżer ocenia i przydziela niezbędne do tego zasoby w postaci
uczestników, szkoleń i narzędzi. By zamknąć realizację projektu w założonych ramach czaso-
wych, menedżer planuje wykorzystywanie związanego z tym wysiłku uczestników w kategoriach
zadań i produktów, a następnie monitoruje status projektu pod względem zgodności z tymi
planami. Wreszcie, by zapewnić należytą jakość produktów, menedżer definiuje mechanizmy
raportowania problemów i monitoruje status produktów w kategoriach usterek i ryzyka. Wszyst-
kie trzy elementy — jakość, budżet i czas — są niezbędnymi składnikami ostatecznego sukcesu.
Trzeba przyznać, że materiał tego rozdziału jest dalece niewystarczający, by na jego
podstawie nauczyć się zarządzania projektami na skalę misji statków kosmicznych. Jego treść
zorientowana jest raczej na znaczącą rolę menedżera nowatorskich projektów informatycz-
nych, o przemysłowej skali zastosowań. Może być także przydatna dla instruktorów nauczają-
cych zespołowej realizacji projektów, a także programistów zamierzających zostać kierownikami
zespołów. Wzorujemy się na modelu dwustopniowej hierarchii organizacyjnej, na której
szczycie plasuje się menedżer projektu, pośredni szczebel zajmują kierownicy zespołów, zaś
na szczeblu najniższym znajdują się programiści. Model taki, notabene typowy dla większości
realizowanych obecnie projektów przemysłowych, okazuje się wystarczający do zilustrowania
złożoności zarządzania takimi projektami.
W sekcji 14.2 prezentujemy zatem ogólny pogląd na zarządzanie projektem i jego związki
z innymi aktywnościami składającymi się na realizację tego projektu. W sekcji 14.3 opisujemy
modele wykorzystywane w związku z zarządzaniem — role, produkty i harmonogramy; mo-
dele te udokumentowane zostają w planie zarządzania projektem. W sekcji 14.4 przedstawiamy
aktywności wchodzące w skład klasycznej, opartej na zespołach, realizacji projektu, zaś sekcję
14.5 poświęcamy „zwinnym" technikom realizowania projektów programistycznych.

14.2. O zarządzaniu projektem ogólnie


W rozdziale 3. „Organizacja projektu i komunikacja" przedstawiliśmy model projektu wi-
dzianego oczami programisty. W kontekście zarządzania projektem zadaniem programisty jest
należyte rozumienie aktywności i artefaktów wynikających z tego zarządzania, a struktura
wykonywanej przez niego pracy nie wykracza poza poziom zleconych mu zadań: artefakty
związane z zarządzaniem są już utworzone i można je tylko wykorzystywać oraz komentować.
640 Rozdział 12. • Zarządzanie racjonalizacją

W t y m rozdziale p r e z e n t u j e m y drugą stronę tego m e d a l u — spojrzenie z perspektywy m e -


n e d ż e r a tworzącego w s p o m n i a n e a r t e f a k t y . Z tej p e r s p e k t y w y p r o j e k t s k ł a d a się z n a s t ę -
pujących komponentów.

• Wynik — t o p o j e d y n c z y p r o d u k t (work product) lub grupa produktów. Produkty


p r z e z n a c z o n e d l a k l i e n t a n a z y w a n e są p r o d u k t a m i f i n a l n y m i (deliverables).

• Praca — t o d z i a ł a n i e p r o w a d z ą c e d o u z y s k a n i a w y n i k u . P r a c a p o d z i e l o n a jest n a
jednostki pracy (work units), którymi są zadania (tasks) i aktywności (activities).
Z a d a n i e m jest n a j m n i e j s z a j e d n o s t k a p r a c y p o d l e g a j ą c a z a r z ą d z a n i u j a k o całość.
Zadania i aktywności m o g ą być rekursywnie g r u p o w a n e z aktywności wyższych
r z ę d ó w . P r a c a o p i s y w a n a jest w p o s t a c i p a k i e t ó w p r a c y (work packages).

• Harmonogram — stanowi odwzorowanie jednostek pracy w okresy upływającego


czasu, n i e k o n i e c z n i e rozłączne. D l a k a ż d e j j e d n o s t k i p r a c y określa się rozpoczęcie,
czas t r w a n i a i z a k o ń c z e n i e .
• Zasoby — ludzie, z w a n i u c z e s t n i k a m i (participants), f u n d u s z e , sprzęt, u m i e j ę t n o ś c i
i możliwości t o p r z y k ł a d y zasobów. K a ż d y uczestnik p e ł n i w projekcie określoną rolę
( l u b k i l k a ról). K a ż d e j roli p r z y p o r z ą d k o w a n y jest j e d e n l u b k i l k a p a k i e t ó w p r a c y .

P r o j e k t y doświadczają tych s a m y c h barier, co systemy i n f o r m a t y c z n e : są złożone i p o -


d a t n e n a z m i a n y . W y t w o r z e n i e z ł o ż o n e g o p r o d u k t u w y m a g a w s p ó ł p r a c y wielu u c z e s t n i k ó w
0 z r ó ż n i c o w a n y c h kwalifikacjach i d o ś w i a d c z e n i u . R y n k o w a k o n k u r e n c j a i e w o l u u j ą c e w y m a -
gania są ź r ó d ł e m k o n i e c z n y c h z m i a n w projekcie, w y m a g a j ą c y c h realokacji z a s o b ó w i u t r u d -
niających śledzenie statusu p r o j e k t u . W p r o w a d z a n i e z m i a n d o p r o j e k t u d o d a t k o w o k o m p l i k u j e
fakt, że jego e l e m e n t y — wynik, h a r m o n o g r a m , p r a c a i zasoby — są ze sobą powiązane, a zmia-
n a w z a k r e s i e j e d n e g o z n i c h s t a j e się ź r ó d ł e m z m i a n w p o z o s t a ł y c h : p r z y k ł a d o w o d o d a n i e
n o w e j cechy d o p r o d u k t u finalnego skutkuje zwykle zwiększeniem czasu jego wytworzenia
1 p o t r z e b n y c h z a s o b ó w , zaś s k r ó c e n i e c z a s u w y t w a r z a n i a p r o d u k t u o d b y w a się zwykle za
c e n ę r e z y g n a c j i z i m p l e m e n t o w a n i a n i e k t ó r y c h j e g o cech.
M e n e d ż e r o w i e r a d z ą sobie ze złożonością p r o j e k t ó w w taki s a m s p o s ó b j a k p r o g r a m i ś c i
ze złożonością s y s t e m u — czyli za p o m o c ą m o d e l o w a n i a , s p r a w n e g o k o m u n i k o w a n i a , racjo-
nalizacji i zarządzania konfiguracją. Kwestie k o m u n i k o w a n i a , racjonalizacji i konfiguracji
o p i s a l i ś m y j u ż w p o p r z e d n i c h rozdziałach, o d p o w i e d n i o 3., 12. i 13., w t y m rozdziale zapre-
z e n t u j e m y r o z m a i t e o b i e k t y o r a z m o d e l e d y n a m i c z n e i f u n k c j o n a l n e o d z w i e r c i e d l a j ą c e za-
r z ą d z a n i e p r o j e k t e m ; a b y o d r ó ż n i ć w s p o m n i a n e m o d e l e o d m o d e l i systemu, b ę d z i e m y nazy-
w a ć je „ m o d e l a m i m e n e d ż e r s k i m i " .
Modele menedżerskie umożliwiają reprezentowanie k o m p o n e n t ó w projektu, ograniczeń
związanych z t y m i k o m p o n e n t a m i oraz p o w i ą z a ń m i ę d z y n i m i . D e k o m p o z y c j a p r a c y n a p r o s t -
sze w z a r z ą d z a n i u z a d a n i a n o s i n a z w ę struktury p o d z i a ł u pracy (work breakdown structure).
M o d e l zadań, odzwierciedlany w postaci diagramu sieciowego, ilustruje zależności czasowe
m i ę d z y p o s z c z e g ó l n y m i z a d a n i a m i . S c h e m a t o r g a n i z a c y j n y (organization chart) u k a z u j e role
pełnione przez poszczególnych uczestników projektu.
P o d s t a w o w e z a d a n i e m e n e d ż e r a t o k o n s t r u o w a n i e , realizowanie i walidacja tych m o d e l i
p r z e z cały czas t r w a n i a p r o j e k t u . Z a p o m o c ą t y c h m o d e l i m e n e d ż e r k o m u n i k u j e k l i e n t o w i
status realizacji projektu, a p r o g r a m i s t o m — zakresy odpowiedzialności przypisywane w związku
z kolejnymi stadiami projektu.
14.2. O zarządzaniu projektem ogólnie 641

Z funkcjonalnego p u n k t u widzenia zarządzanie projektem m o ż e być zdefiniowane jako


proces prowadzenia p r o j e k t u o d początku d o końca, przy użyciu zbioru modeli m e n e d ż e r -
skich. Z a r z ą d z a n i e p r o j e k t e m o b e j m u j e cztery p o d s t a w o w e aktywności:

• Planowanie, czyli specyfikowanie celów d o osiągnięcia oraz aktywności i zadań p r o -


wadzących d o ich uzyskania, a także określenie h a r m o n o g r a m u i oszacowanie nie-
zbędnych z a s o b ó w ludzkich i m a t e r i a l n y c h .
• Organizowanie, polegające n a zdefiniowaniu organizacji projektu oraz przypisaniu
ról i zakresów odpowiedzialności. Zadania i aktywności związane z poszczególnymi
r o l a m i d e f i n i o w a n e są n a etapie p l a n o w a n i a .

• Kontrolowanie, k o n c e n t r u j ą c e się n a w y k r y w a n i u rozbieżności m i ę d z y a k t y w n o -


ściami zaplanowanymi a bieżąco wykonywanymi, czyli między i n n y m i n a potwier-
dzaniu oczekiwanej wydajności uczestników, m o n i t o r o w a n i u p o d e j m o w a n y c h akcji
i osiąganych rezultatów, określaniu sposobów postępowania z napotykanymi p r o -
blemami i udostępnianiu zainteresowanym uczestnikom informacji związanych
z p r o j e k t e m . W p r z y p a d k u zaistnienia w s p o m n i a n e j r o z b i e ż n o ś c i w z n a c z ą c y m
s t o p n i u m e n e d ż e r z m u s z o n y jest d o z m i a n y p r z y d z i a ł u zasobów, z r e w i d o w a n i a
h a r m o n o g r a m u lub renegocjowania oczekiwanych wyników.

• Kończenie — ta aktywność k o n c e n t r u j e się n a działaniach związanych z k o ń c o w y m


stadium projektu: na dostarczeniu klientowi systemu spełniającego kryteria akcepta-
cyjne określone w u m o w i e projektu, zainstalowaniu systemu w środowisku klienta,
d o k o n a n i u przeglądu historii projektu w celu wyciągnięcia w n i o s k ó w n a przyszłość
orz z m o d y f i k o w a n i u szablonów projektowych n a potrzeby następnej iteracji i (lub)
kolejnego p r o j e k t u .

Z dynamicznego p u n k t u widzenia projekt znajduje się w danej chwili w jednej z określo-


nych faz, które opisaliśmy w rozdziale 3. (patrz rysunek 3.2). Rozszerzymy ów dynamiczny model
p r o j e k t u przez d o d a n i e jeszcze j e d n e g o s t a n u — concepti on (patrz r y s u n e k 14.1).

Rysunek 14.1. Fazy projektu programistycznego na diagramie stanów UML


642 Rozdział 12. • Zarządzanie racjonalizacją

Faza koncepcyjna. W trakcie tej fazy rodzi się idea projektu. W przypadku projektu
programistycznego idea ta jest zwykle konsekwencją nowych wymagań dyktowanych przez re-
alia rynkowe lub nowe technologie. Aktywności fazy koncepcyjnej mogą mieć rozmaitą postać:
dla małych projektów może to być nieformalna dyskusja zainteresowanych osób, uwieńczona
werbalnym porozumieniem.
W przypadku dużych projektów ich idea staje się przedmiotem wszechstronnej analizy,
przede wszystkim porównania kosztów ze spodziewanymi zyskami oraz technicznego stu-
dium opłacalności. Szeroko rozumiane pojęcie „zysku" może oznaczać zmniejszenie kosztów
operacyjnych, uzyskanie takiego samego zysku przy mniejszym obrocie, podniesienie morale
pracowników, zwiększenie ich produktywności i zmniejszenie absencji.
Podstawowym problemem technicznej analizy opłacalności jest pytanie, czy wymagane
innowacje staną się dostępne w trakcie realizacji projektu. Przejście do następnej fazy projektu
wymaga formalnej decyzji, poprzedzonej formalnym przeglądem wyników wspomnianych
analiz.
Faza definicyjna. W tę fazę zaangażowani są menedżer projektu, klient oraz architekt
systemu. Menedżer projektu może być jedną z zainteresowanych osób uczestniczących w fazie
koncepcyjnej lub (co jest częstsze) wywodzić się spoza tego grona. Faza definicyjna projektu
obejmuje następujące aktywności:

• Definiowanie problemu. Klient i menedżer projektu definiują zakres systemu w ka-


tegoriach jego funkcjonalności, ograniczeń i produktów finalnych. Klient i menedżer
projektu ustalają także daty dostarczenia owych produktów i kryteria ich akceptacji
przez klienta. Rezultatem tych ustaleń jest dokument zwany deklaracją problemu
(problem statement), służący jako punkt wyjścia dla dokumentu analizy wymagań
(RAD), opracowywanego w fazie ustalonej projektu.
• Wstępne zaplanowanie zarządzania projektem. W dokumencie o nazwie Wstępny
Plan Zarządzania Projektem Programistycznym (SPMP — Software Project Mana-
gement Plan) menedżer przedstawia ogólny kształt projektu i rezultaty, jakie mają być
osiągnięte w jego ramach, strukturę podziału pracy, role i zakresy odpowiedzialności
oraz metody identyfikowania ryzyka i radzenia sobie z nim.
• Wstępne definiowanie architektury systemu. Ta aktywność przebiega równolegle
z fazą opracowywania SPMP; jej zadaniem jest określenie architektury systemu,
a szczególnie jego dekompozycji na podsystemy. Architektura oprogramowania jest
czynnikiem krytycznym z perspektywy zarządzania projektem, stanowi bowiem
podstawę do wstępnego formowania zespołów. Definiowanie architektury systemu
wymaga więc ścisłego współdziałania architekta oprogramowania z menedżerem
projektu. Architekt dokonuje rewizji dekompozycji w fazie ustalonej projektu, Do-
kument opisujący wstępną architekturę systemu stanowi zaczątek przyszłego do-
kumentu projektu systemu (SDD).
• Uzgodnienie umowy projektu. Menedżer projektu i klient formalnie porozumie-
wają się w sprawie zakresu działania systemu oraz terminów dostarczania jego pro-
duktów finalnych. Wyrazem tego porozumienia jest dokument umowy projektu
(Project Agreement).
14.2. O zarządzaniu projektem ogólnie 643

Faza startowa. W tej fazie menedżer projektu buduje jego infrastrukturę, zatrudnia uczest-
ników, organizuje ich w zespoły i uruchamia realizację projektu. Faza ta wiąże się z następują-
cymi aktywnościami:

• Budowanie infrastruktury. Infrastruktura obejmuje kanały komunikacji między


uczestnikami, takie jak tablice ogłoszeniowe, serwisy WWW, narzędzia zarządzania
konfiguracją, środowiska do tworzenia i testowania oprogramowania oraz procedury
organizowania spotkań z menedżerem i przepływ pracy związanej z przeglądami i auto-
ryzowaniem dokumentów. Menedżer udostępnia powyższe założenia zespołowi infra-
struktury, który ją fizycznie tworzy i utrzymuje przez cały czas realizacji projektu 3 .
• Rozpoznawanie kwalifikacji. Menedżer projektu identyfikuje umiejętności oraz
zainteresowania programistów i dokumentuje je w formie macierzy kwalifikacji.
• Sformowanie zespołów. Wykorzystując macierz kwalifikacji, menedżer projektu
przydziela uczestników do zespołów zajmujących się poszczególnymi podsystemami,
wyodrębnionymi w ramach wstępnego identyfikowania architektury systemu, oraz
do zespołów międzyfunkcyjnych. W każdym zespole wybierany jest kierownik. Na-
stępnie menedżer i kierownicy zespołów dokonują przydziału ról i zakresów od-
powiedzialności dla poszczególnych uczestników. Gdy umiejętności uczestników są
niewystarczające do pełnienia przez nich przydzielonych ról, menedżer projektu
identyfikuje potrzeby w zakresie dodatkowych szkoleń i tutoriali dla uczestników.
Na końcu poszczególnym zespołom przydzielone zostają pakiety pracy.
• Uruchomienie projektu. Menedżer projektu, kierownicy zespołów i klient oficjalnie
ogłaszają rozpoczęcie realizacji projektu na zebraniu z udziałem wszystkich uczestni-
ków. Celem tego zebrania jest zaznajomienie uczestników z zakresem projektu, jego
infrastrukturą komunikacyjną i zakresem odpowiedzialności każdego z zespołów.
Realizacja projektu wchodzi w fazę ustaloną.

Faza ustalona. W fazach definicyjnej i startowej projektu większość decyzji podejmo-


wana jest przez menedżera. W fazie ustalonej część funkcji menedżerskich przejmują kierow-
nicy zespołów. Stają się odpowiedzialni między innymi za śledzenie statusu prac swych
zespołów i identyfikowanie problemów zgłaszanych na zebraniach tych zespołów. Kierownicy
zespołów raportują status menedżerowi projektu, który na tej podstawie ocenia stan projektu
jako całości. Na barki kierowników zespołów spada też obowiązek zmiany przydziału zadań
i uzyskiwania dodatkowych zasobów od menedżera, gdy okaże się konieczna zmiana planów.
Menedżer projektu jest natomiast odpowiedzialny za współdziałanie z klientem, uzyskiwanie
formalnych porozumień oraz renegocjowanie zasobów i terminów. Faza ustalona projektu
obejmuje następujące aktywności:

• Definiowanie zakresu projektu. Gdy tylko model analityczny stanie się stabilny,
klient i menedżer projektu formalnie porozumiewają się w kwestii funkcyjnych i po-
zafunkcyjnych wymagań dla systemu, co może powodować aktualizację dokumentu
umowy projektu.

3
Zależnie od rozmiaru i złożoności projektu w skład jego infrastruktury mogą wchodzić także sys-
temy wspomagające zarządzanie finansami i kadrą uczestników. Nie będziemy się nimi zajmować
w tej książce.
644 Rozdział 14. ® Zarządzanie projektem

• Kontrolowanie. Przez wszystkie pozostałe fazy projektu kierownicy zespołów i me-


nedżer projektu monitorują status jego realizacji na cotygodniowych zebraniach,
w trakcie których porównują go z harmonogramem zdefiniowanym w dokumencie
SPMP. Kierownicy zespołów odpowiedzialni są za kolekcjonowanie informacji
0 statusie — w ramach zebrań, przeglądów, raportowania problemów i w miarę
kompletowania poszczególnych produktów — i raportowanie tej informacji mene-
dżerowi projektu.
• Zarządzanie ryzykiem. Aktywność ta ma na celu identyfikowanie potencjalnych
problemów, które niosą ze sobą ryzyko opóźnień lub przekroczenia budżetu. Mene-
dżer projektu, przy współpracy kierowników zespołów, rozpoznaje potencjalne
problemy i analizuje je pod kątem stopnia wspomnianego ryzyka, sporządza też
stosowne plany awaryjne.
• Modyfikowanie planów projektu. Gdy realizacja projektu odbiega od harmonogra-
mu lub uruchomiony zostaje plan awaryjny, menedżer projektu zmuszony jest do
zrewidowania harmonogramu i realokacji zasobów w celu terminowego dostarczenia
produktów finalnych. Może on w związku z tym zatrudniać nowych programistów,
tworzyć nowe zespoły lub łączyć istniejące. Zmiana planów projektu może stać się
konieczna również w przypadku, gdy klient zmodyfikuje swe wymagania.

Faza końcowa. W tej fazie klient otrzymuje produkty finalne, kolekcjonowana jest także
historia projektu. Większość zaangażowania programistów w projekt kończy się tuż przed
rozpoczęciem tej fazy, gdy doprowadzą modele oraz kod źródłowy do ostatecznej postaci
i skompletują dokumentację. Grupa programistów, dokumentaliści i kierownicy zespołów
zaangażowani zostają w zainstalowanie systemu u klienta i przygotowanie tego systemu do
testów akceptacyjnych. Dokumentaliści kolekcjonują historię projektu na użytek przyszłych
projektów. Z fazą końcową projektu wiążą się następujące aktywności:

• Dostarczenie systemu, polegające na wykonaniu przez klienta testów akceptacyjnych


1 zainstalowaniu systemu u klienta, zazwyczaj w tej właśnie kolejności.
• Testowanie akceptacyjne. System zostaje oceniony przez klienta pod kątem
spełnienia kryteriów akceptacyjnych zdefiniowanych w umowie projektu: speł-
nienie wymagań funkcyjnych i pozafunkcyjnych demonstrowane jest i testowane
przy użyciu scenariuszy zdefiniowanych we wspomnianej umowie.
• Instalowanie. Po formalnym zaakceptowaniu przez klienta system instalowany
jest w środowisku docelowym i dostarczona zostaje jego dokumentacja. Instalacja
systemu może obejmować także szkolenie użytkowników oraz migrację danych
z systemu dotychczas eksploatowanego.
• Post mortem. Menedżer projektu i kierownicy zespołów kolekcjonują historię pro-
jektu w celu wyciągnięcia z niej różnorodnych wniosków. Dokumentując większe
i mniejsze niepowodzenia wraz z analizą ich przyczyn, można uchronić się przed
powtórnym popełnianiem tych samych błędów.
14.2. O zarządzaniu projektem ogólnie 645

Każda z wymienionych faz pozwala na znaczące zrównoleglenie aktywności menedżer-


skich, co zilustrowane zostało na diagramie aktywności, widocznym na rysunku 14.2. W dalej
w tym rozdziale opiszemy wspomniane aktywności bardziej szczegółowo: w następnej sekcji
przedstawimy modele zarządzania projektem używane w różnych fazach jego realizacji, w celu
określenia między innymi pracy do wykonania, harmonogramów i organizacji zespołowej.
Pokażemy także, jak dokumentować te modele w planie zarządzania projektem (SPMP).

Rysunek 14.2. Aktywności związane z zarządzaniem projektem programistycznym


646 Rozdział 12. • Zarządzanie racjonalizacją

1 4 3 . Koncepcje zarządzania projektem


Podstawowym założeniem, domyślnie przyjmowanym podczas planowania projektu, jest nie-
możność jego zrealizowania w postaci pojedynczej, monolitycznej aktywności —gdyby istniała
taka możliwość, należałoby się wziąć do pracy, zamiast tracić czas na zbędną biurokrację. Skoro
nie istnieje, musimy uporać się z projektem na zasadzie „dziel i zwyciężaj", dekomponując
pracę nad jego realizacją na mniejsze jednostki, prostsze w realizacji.
Wspomniana dekompozycja, jako jedno z kluczowych zadań planowania, wiąże się ze
zdefiniowaniem dwóch rzeczy: zadań do wykonania oraz zależności między nimi. Zajmiemy
się najpierw samymi zadaniami: hierarchiczna reprezentacja ich zbioru nosi nazwę struktury
podziału pracy (WBS — Work Breakdown Structure). Następnie zajmiemy się reprezentacją
zależności czasowych między zadaniami, czyli modelem zadań. Model zadań odwzorowany
w oś upływającego czasu nosi nazwę harmonogramu (schedule).

14.3.1. Zadania i aktywności


Zadaniem (task) nazywamy dobrze zdefiniowany przydział pracy dla programisty lub zespołu
programistów. Zadanie jest najmniejszą, niepodzielną jednostką podlegającą zarządzaniu.
Kluczowe elementy zadania to jego opis, czas trwania i przypisanie do określonej roli pełnionej
przez uczestnika. Realizacja zadania wiąże się ze zużywaniem zasobów i tworzeniem produk-
tów. Przykłady prostych zadań przedstawiliśmy w tabeli 14.1.
Grupa powiązanych zadań nosi nazwę aktywności (activity). Aktywności są złożonymi
jednostki pracy; czas trwania aktywności rozciąga się przeważnie na całą fazę projektu, a nie-
kiedy na cały projekt — aktywności tej drugiej grupy nazywamy funkcjami projektowymi
(project functions). Przykładami funkcji projektowych są zarządzanie konfiguracją i (jakby
nie było) samo zarządzanie projektem. Zależność między zadaniem, aktywnością i funkcją
projektową przedstawiono na rysunku 14.3.

14.3.2. Produkty, pakiety pracy i role


Menedżer przyporządkowuje pojedynczym uczestnikom lub zespołom zadania, aktywności
i funkcje projektowe do wykonania, a następnie monitoruje postępy w ich realizacji. W małych
projektach przyporządkowanie takie może odbywać się nieformalnie, na skinięcie ręki: w dużych
projektach przyjmuje jawną postać pakietów pracy (work packages — patrz rysunek 14.4).
Elementem pakietu pracy jest lista wynikowych produktów, zasobów niezbędnych do wy-
konywania pracy, oczekiwany czas trwania tej pracy oraz specyfikacja danych wejściowych
(którymi mogą być produkty wytwarzane w ramach innych zadań oraz zależności od innych
zadań). Dla pakietu pracy definiuje się też kryteria akceptacyjne i odpowiedzialną za niego
osobę lub jednostkę organizacyjną.
Pakiety pracy są ważnymi artefaktami menedżerskimi, stanowią bowiem formę przypo-
rządkowywania pracy do wykonania. Przyporządkowanie to powinno odbyć się zaraz po zi-
dentyfikowaniu odnośnych zadań. Przykładami małych pakietów pracy są elementy działania
(action items), z których każdy zawiera jedynie opis zadania, nazwę odpowiedzialnego uczest-
nika i czas, w którym zadanie ma być wykonane. Przykłady bardziej złożonych pakietów
14.3. Koncepcje zarządzania projektem 647

Tabela 14,1. Przykłady zadań związanych z realizacją podsystemu bazy danych

Wynika
N a z w a zadania Opis Wejście Wyjście
z roli
Zbieranie Projektant Zbieranie od Łącznicy. API podsystemu,
wvmaaafi dla systemu. poszczególnych model analityczny
podsystemu zespołów wymagań obiektów
zarządzania
dotyczących trwałych.
bazą danych
przechowywania
obiektów,
identyfikowanie
obiektów trwałych.

Projektowanie Projektant Projektowanie API Projekt


podsystemu podsystemu. podsystemu podsystemu. podsystemu
zarządzania z uwzględnieniem bazy danych.
bazą danych
wyboru spośród
rozwiązań
komercyjnych.

Implementowanie Implementator. Implementacja Projekt Kod źródłowy


podsystemu podsystemu bazy podsystemu. podsystemu.
zarządzania danych.
bazą danych
Inspekc.ia kodu Implementator. Przeprowadzenie Kod źródłowy Lista wykrytych
podsystemu inspekcji kodu podsystemu. usterek.
zarządzania źródłowego
bazą danych
podsystemu.
Planowanie Tester. Opracowanie Kod źródłowy Zestawy testowe
testów zestawów testowych podsystemu. i plan
podsystemu dla podsystemu bazy testowania.
zarządzania
danych.
bazą danych
Testowanie Tester. Wykonanie testów Plan testowania W y n i k i testów,
podsystemu podsystemu bazy podsystemu, lista wykrytych
zarządzania danych. zestawy testowe usterek.
bazą danych
dla podsystemu.

Rysunek 14.3. Zadania, aktywności i funkcje projektowe


648 Rozdział 12. • Zarządzanie racjonalizacją

Rysunek 14.4. Relacje między wynikiem, pracą i pakietem pracy

pracy to opisy tworzenia modeli obiektowych, diagramów klas oraz sekcji dokumentów lub
kompletnych dokumentów — i tak każda sekcja dokumentu analizy wymagań może odpowia-
dać pakietowi pracy określającemu jej autora i czas sporządzania.
Produkt przeznaczony dla użytkownika nazywany jest produktem finalnym (deliverable);
przykładami produktów finalnych są sam system będący wynikiem projektu i jego doku-
mentacja. Wszystkie inne produkty, tworzone na użytek innych uczestników projektu, nazy-
wamy produktami wewnętrznymi (internal work products). Elementy działania są przykładem
produktów wewnętrznych.

14.3.3. Struktura podziału pracy


Hierarchiczna reprezantacja zbioru wszystkich zadań związanych z projektem nazywana jest
strukturą podziału pracy (WBS — work breakdown structure). Jest ona uproszczonym mo-
delem pracy czekającej na wykonanie, ponieważ odzwierciedla jedynie agregację zidentyfi-
kowanych zadań (patrz rysunek 14.5). Na rysunku 14.6 widoczna jest część struktury podziału
pracy związanej z budową domu; aktywności widoczne z prawej strony mogą być przyporząd-
kowane różnym podwykonawcom. Koszt zagregowanej aktywności (Budowanie struktury)
jest agregacją k o s z t ó w jej aktywności s k ł a d o w y c h (Budowani e fundamentów, Budowani e ści an,
Budowanie dachu). W ę z e ł n a szczycie hierarchii (Budowanie domu) r e p r e z e n t u j e całość p r a c y
potrzebnej do zrealizowania projektu.

Rysunek 14.5. Struktura podziału pracy jako agregacja wszystkich prac składających się na realizację
projektu
14.3. Koncepcje zarządzania projektem 649

Rysunek 14.6. Częściowa struktura podziału pracy związanej z budową d o m u

Zauważmy, że struktura podziału pracy w żaden sposób nie odzwierciedla kolejności


wykonywania poszczególnych prac, przykładowo Ins tal owani e kanał izacji i Instalowanie
s i e c i el ektrycznej mogą być realizowane równolegle, mimo iż należą do różnych gałęzi
wspomnianej struktury. Zależności czasowe między zadaniami odzwierciedlane są bowiem
w postaci modeli zadań.

14.3.4. Model zadań


Zadania są powiązane uwarunkowaniami czasowymi, przykładowo zadanie Budowanie dachu
nie może rozpocząć się przed zakończeniem zadania Budowanie ścian. Zależności czasowe
między zadaniami odzwierciedlane są w postaci modelu zadań (task model), zwanego także
diagramem sieciowym (network diagram). Na rysunku 14.7 przedstawiony jest model zadań
z tabeli 14.1.
Dla każdego zadania wyznaczony jest czas trwania, oszacowany przez menedżera pro-
jektu na podstawie specyfiki zadania i liczby uczestników przydzielonych do jego realizacji. Osza-
cowania tego dokonuje on jeszcze przed uruchomieniem projektu; znając zależności czasowe
między zadaniami, może wyliczyć najkrótszy możliwy czas realizacji całego projektu. Jest to
sumaryczny czas realizacji zadań leżących na najdłuższej ścieżce w grafie modelu zadań, zwanej
ścieżką krytyczną (criticalpath). Przymiotnik „krytyczna" wynika z faktu, że opóźnienie jakie-
gokolwiek należącego do niej zadania przekłada się na opóźnienie całego projektu. Dla do-
wolnego zadania można definiować tak zwany czas przestoju (slack time) jako maksymalny
czas, o jaki może opóźnić się to zadanie bez konsekwencji w postaci opóźnienia całego pro-
jektu. Dla zadań leżących na ścieżce krytycznej czas ten jest z definicji równy zero.
Ograniczenia czasowe mogą być wykorzystywane do określenia rozpoczęcia i zakończe-
nia danego zadania, niezależnie od jego relacji z innymi zadaniami. Ograniczenia takie wynikają
zwykle z ustaleń między klientem a menedżerem projektu. Przykładowo wymóg zakończenia
zadania w pierwszym tygodniu grudnia jest ograniczeniem typu MFO; jeśli zadanie musi roz-
począć się w poniedziałek, jest to ograniczenie typu MSO (patrz tabela 14.2). Jest zrozumiałe,
że ograniczenie ASAP jest najbardziej atrakcyjne dla menedżera projektu, w przeciwieństwie
do programistów preferujących zwykle ograniczenie ALAP.
650 Rozdział 12. • Zarządzanie racjonalizacją

Rysunek 14.7. Zależności czasowe między zadaniami wymienionymi w tabeli 14.1

Tabela 14.2. Typy ograniczeń czasowych

Ograniczenie Znaczenie
ASAP Możliwie najwcześniej (As soon as possible)
ALAP Możliwie najpóźniej (As late as possible)
FNET Zakończyć nie wcześniej niż (Finish no earlier than)
SNET Rozpocząć nie wcześniej niż (Start no earlier than)
FNLT Zakończyć nie później niż (Finish no later than)
SNLT Rozpocząć nie później niż (Start no later than)
MFO Wymagany moment zakończenia (Must finish on)
MSO Wymagany moment rozpoczęcia (Must start on)

14.3.5. Macierz kwalifikacji


G d y tylko d o k o n a n y zostanie podział pracy, m e n e d ż e r m u s i znaleźć uczestników właściwych
d o w y k o n a n i a poszczególnych zadań. Jest to dla niego o tyle t r u d n e , że m o ż e w ogóle nie znać
u c z e s t n i k ó w p r o j e k t u ; i n w e s t u j ą c j e d n a k s w ó j wysiłek w r o z p o z n a n i e u m i e j ę t n o ś c i , w i e d z y
i zainteresowań poszczególnych uczestników, m o ż e przyczynić się d o lepszego wykorzystania
ich t a l e n t ó w i o g ó l n i e d o p o l e p s z e n i a ich p r o d u k t y w n o ś c i .
W y g o d n ą f o r m ą o d w z o r o w a n i a kwalifikacji uczestników w j e d n o s t k i pracy — zadania,
aktywności i f u n k c j e projektowe — jest macierz kwalifikacji (skill matrix — patrz tabela 14.3).
Jej w i e r s z e o d p o w i a d a j ą j e d n o s t k o m p r a c y , zaś k o l u m n y r e p r e z e n t u j ą p o s z c z e g ó l n y c h
uczestników; k o m ó r k i macierzy identyfikują przydatność uczestnika w kontekście odpowiedniej
14.3. Koncepcje zarządzania projektem 651

Tabela 14.3. Przykładowa macierz kwalifikacji dla czterech zadań i czterech uczestników
• — przydatność pierwszorzędna O — przydatność drugorzędna — zainteresowanie

Zadanie/Uczestnik Bill Mary Sue Ed


Projektowanie przepływu sterowania A A
Projektowanie bazy danych A A •
Projektowanie interfejsu użytkownika o A
Zarządzanie konfiguracją 0 A
jednostki pracy; r o z r ó ż n i a m y trzy p o z i o m y tej przydatności: pierwszorzędną (primary), dru-
gorzędną (secondary) i zainteresowanie (interest). Przydatność pierwszorzędna kwalifikuje
uczestnika d o roli kierowniczej, przydatność drugorzędna —• d o partycypacji w zadaniu. Zain-
t e r e s o w a n i e d a n ą t e m a t y k ą n i e k o n i e c z n i e m u s i w i ą z a ć się z u m i e j ę t n o ś c i a m i czy w i e d z ą
w jej zakresie.

14.3.6. Plan zarządzania projektem


N a rysunku 14.8 przedstawiliśmy skrótowo opisane dotychczas modele zarządzania; stanowią
o n e efekt wzbogacenia i uszczegółowienia modeli opisywanych w rozdziale 3. Użyliśmy trzy-
krotnie wzorca projektowego Kompozyt w celu generalizacji klas Produkt, Zadani e i Uczestni k.
Dodaliśmy też klasy S t r u k t u r a podzi a?u pracy i Organi z a c j a , które nie są zwykle widoczne
dla p r o g r a m i s t ó w .

Rysunek 14.8. Model projektu z punktu widzenia jego menedżera; stanowi wzbogacenie modelu
opisywanego w rozdziale 3. o trzykrotne zastosowanie wzorca Kompozyt do generalizacji klas Produkt,
Zadanie i Uczestni k
652 Rozdział 12. • Zarządzanie racjonalizacją

Streszczenie modeli zarządzania jest częścią Planu Zarządzania Projektem (SPMP)


[IEEE Std. 1058-1998]. W przypadku projektu „kaskadowego"4 dokument ten tworzony jest
jeszcze przed uruchomieniem projektu, dla projektu ze scentralizowaną architekturą jest
budowany po zdefiniowaniu architektury oprogramowania. Gdy stosowane są „zwinne" tech-
niki programowania, dokument ten jest aktualizowany po każdej iteracji. Przykładowa struktura
SPMP może wyglądać następująco:

1. Streszczenie
1.1. Podsumowanie projektu
1.2. Ewolucja planu
2. Odwołania
3. Definicje
4. Organizacja projektu
4.1. Interfejsy zewnętrzne
4.2. Struktura wewnętrzna
4.3. Role i zakresy odpowiedzialności
5. Plan procesu zarządzania
5.1. Plan rozpoczęcia
5.2. Plan pracy
5.3. Plan kontroli
5.4. Plan zarządzania ryzykiem
5.5. Plan zamknięcia
6. Plan procesów technicznych
6.1. Model procesu
6.2. Metody, narzędzia i techniki
6.3. Plan infrastruktury
6.4. Plan akceptacji produktów
7. Plan procesów wspomagających
7.1. Plan zarządzania konfiguracją
7.2. Plan weryfikacji i walidacji
7.3. Plan dokumentacji
7.4. Plan zapewnienia jakości
7.5. Przeglądy i audyty
7.6. Plan rozwiązywania problemów
7.7. Plan zarządzania podwykonawcami
7.8. Plan usprawnienia procesu
8. Dodatkowe plany

Odbiorcami SPMP są głównie programiści i menedżer projektu. SPMP dokumentuje


wszystkie zagadnienia związane z wymaganiami klienta (między innymi produktami finalnymi
i kryteriami ich akceptacji), celami projektowymi, organizacją projektu, podziałem pracy na
zadania, przydziałem zasobów i przydzieleniem odpowiedzialności. Pierwsze trzy sekcje do-
kumentu dostarczają ogólne informacje na temat zawartości następnych sekcji: opisują skró-
towo projekt i jego „kamienie milowe", produkty finalne i spodziewane zmiany dokumentu.

4
Model kaskadowy opisujemy w sekcji 15.4.1 — przyp. tłum.
14.4. Aktywności klasycznego zarządzania projektem 653

tem w umowie projektu.

łów dedykowanych poszczególny^ podsystemom i zespołów międzyfonkcyjnycL Zdefmio-

^Sekcja szósta wyznacza techniczne standardy dla wszystkich zespołów. Standardy te

nia, infrastrukturę i plany akceptacji produktów.

definicyjnej, w koordynacji z dokumentem początkowej architektury oprogramowania,

projektu. Zalecamy bowiem nadanie tym dokumentom statusu linii bazowej dopiero po za-

historię zmian: każda zmiana odnotowana zostaje w postaci krótkiego opisu, autora i daty. Do-
kument SPMP powinien być aktualizowany po podjęciu każdej decyzji mogącej wpływać na
wynik projektu, a także po wystąpieniu istotnych problemów. W dużych projektach aktual-
ność SPMP dedykowana jest wybranej osobie pełniącej rolę planisty. W przypadku projektów

nowiącej bazę dla planowania projektu wersji objętej zarządzaniem konfiguracją po podpisa

14.4. Aktywności klasycznego zarządzania projektem

fikowanie produktów, zadań i ról. Kierownicy zespołów dołączają do projektu pod koniec fazy
654 Rozdział 12. • Zarządzanie racjonalizacją

Kierownicy zespołów powinni być jednak świadomi aktywności poprzedzających ich


zaangażowanie w projekt, a także tych, które wynikają z interakcji z klientem. Przedyskutujemy
w związku z tym następujące aktywności menedżerskie:

• Planowanie projektu (patrz sekcja 14.4.1): zdefiniowanie problemu, określenie po-


czątkowej postaci modelu zadań i struktury organizacyjnej, oszacowanie potrzebnych
zasobów ludzkich i materialnych.
• Organizowanie projektu (patrz sekcja 14.4.2): zatrudnienie uczestników, identyfikacja
ich kwalifikacji, przydzielenie im ról i zakresów odpowiedzialności oraz zorganizo-
wanie tutoriali i zebrania uruchamiającego projekt.
• Kontrolowanie projektu (patrz sekcja 14.4.3): monitorowanie projektu, zarządzanie
ryzykiem, sformalizowanie umowy projektu.
• Kończenie projektu (patrz sekcja 14.4.4): testy akceptacyjne, instalacja systemu, sesja
post mortem.

W tej sekcji skupimy się na projektach typu klasycznego, w których charakter produktów
finalnych jest względnie dobrze zdefiniowany już na początku projektu, a celem projektu jest
ich dostarczenie w ustalonym terminie i za cenę kosztów określonych w budżecie. W sekcji
14.5 powrócimy do opisywanych tu aktywności, by przedstawić ich obraz w przypadku „zwin-
nego" projektu, którego zakres oraz wymagania dotyczące produktów finalnych definiowane są
sukcesywnie w miarę postępu w realizacji projektu, same zaś produkty dostarczane są w sposób
przyrostowy.

14.4.1. Planowanie projektu


Planowanie projektu skupia się w fazie definicyjnej, obejmuje zdefiniowanie problemu, zapla-
nowanie jego rozwiązania i oszacowanie potrzebnych do tego zasobów, a skutkuje produktami
(patrz rysunek 14.9), które omawiamy niżej.

• Deklaracja problemu to krótki dokument opisujący problem, dla rozwiązania którego


tworzony jest system będący przedmiotem projektu, a także docelowe środowisko
dla tego systemu, jego produkty finalne i kryteria ich akceptacji przez klienta. Dekla-
racja problemu to jedynie ogólny opis, stanowiący zalążek następnych dokumentów:
umowy projektu, formalizującej wspólne zrozumienie projektu przez jego menedżera
i klienta, oraz dokumentu analizy wymagań (RAD) opisującego tworzony system
w sposób precyzyjny.
• Wysokopoziomowy projekt systemu to reprezentacja jego początkowej dekompo-
zycji na podsystemy, stanowiąca podstawę ich przydziału do poszczególnych zespołów.
Jest jednocześnie zalążkiem dla późniejszego dokumentu projektu systemu (SDD)
opisującego w sposób precyzyjny architekturę tworzonego oprogramowania.
• Plan zarządzania projektem (SPMP) to ujęcie wszystkich menedżerskich aspektów
projektu, między innymi struktury podziału pracy, harmonogramu, organizacji, pa-
kietów pracy i budżetu.
14.4. Aktywności klasycznego zarządzania projektem 655

Rysunek 14.9. Produkty generowane w wyniku planowania projektu i ich powiązania z typowymi
produktami finalnymi

Usytuowanie projektu architektury oprogramowania na samym początku planowania


nosi nazwę zarządzania scentralizowanego architektonicznie (architecture-centric management).
Równoległe sporządzanie SPMP i projektu architektury jest wysoce pożądane, jednakże prze-
sunięcie dekompozycji systemu do fazy organizacyjnej projektu jest już odstępstwem od
powszechnie praktykowanych sposobów zarządzania projektami programistycznymi. W śro-
dowiskach uniwersyteckich i w przypadku małych projektów obie role — menedżera projektu
i architekta oprogramowania — pełni zwykle ta sama osoba; wobec związanego z tym nakładu
pracy musi to być osoba wysoce zmotywowana i charakteryzująca się kwalifikacjami technicz-
nymi. W dużym projekcie nigdy nie łączy się tych ról. Architekt i menedżer projektu powinni
ściśle współpracować z zespołem decyzyjnym, w celu wyraźnego podziału podejmowanych
decyzji na techniczne i menedżerskie [Paulish, 2001].

Opracowywanie deklaracji problemu

Deklaracja problemu (Problem Statement) opracowywana jest wspólnie przez menedżera


projektu i klienta jako wyraz zgodnego rozumienia problemu oczekującego na rozwiązanie
przez tworzony system. Deklaracja problemu opisuje bieżącą sytuację (w kontekście wspo-
mnianego problemu), elementy funkcjonalne prowadzące do rozwiązania wspomnianego
problemu, produkty finalne oczekiwane przez klienta wraz z terminami ich dostarczenia
i kryteriami akceptacji. W deklaracji problemu mogą też być zawarte ograniczenia pod wzglę-
dem na przykład używanego środowiska programistycznego czy języka programowania.
656 Rozdział 12. • Zarządzanie racjonalizacją

Deklaracja problemu nie jest w żadnym razie precyzyjną ani kompletną specyfikacją sys-
temu, a raczej wysokopoziomowym podsumowaniem dwóch przyszłych dokumentów
— RAD i SPMP.
Menedżer projektu i klient opracowują deklarację problemu w sposób iteracyjny, w drodze
zarówno negocjowania, jak i zbierania informacji, i w konsekwencji wzajemnego poznawania
oczekiwań oraz ograniczeń. Poza wysokopoziomowym opisem funkcjonalności tworzonego
sytemu, deklaracja problemu może także zawierać przykłady konkretnych sytuacji, rozwiązań
i tym podobnych, co dodatkowo pomaga w upewnieniu się, że obie strony mają taką samą wi-
zję przedmiotowego problemu. Aktualna i przyszła sytuacja w kontekście tego problemu mogą
być ilustrowane konkretnymi scenariuszami (patrz rozdział 4. „Zbieranie wymagań").
Struktura typowej deklaracji problemu nie jest zbyt rozbudowana. Zauważmy przy tym,
że sekcje druga, trzecia i czwarta nie zawierają precyzyjnej specyfikacji systemu, a stanowią
jedynie podstawę do zbierania wymagań.

DEKLARACJA PROBLEMU
1. Dziedzina aplikacyjna
2. Scenariusze
3. Wymagania funkcyjne
4. Wymagania pozafunkcyjne
5. Środowisko docelowe
6. Produkty finalne i terminy

Pierwsza sekcja opisuje domenę problemu, w drugiej zawarte są przykładowe scena-


riusze ilustrujące ów problem z perspektywy użytkownika oraz jego interakcje z przyszłym
systemem. Sekcje trzecia i czwarta poświęcone są streszczeniu wymagań (odpowiednio) funk-
cyjnych i pozafunkcyjnych — te ostatnie odzwierciedlają ograniczenia narzucone przez klienta,
takie jak użycie konkretnego języka programowania, wykorzystanie gotowych komponentów
czy dedykowanie testowania odrębnemu zespołowi. Pod pojęciem „środowiska" opisywanego
w sekcji piątej rozumiemy między innymi sprzęt i platformę systemową oraz użytkowników
przyszłego systemu. Całość zamyka sekcja szósta, w której wymienione są produkty finalne
oraz terminy dostarczenia ich klientowi.

Definiowanie projektu wysokopoziomowego

Projekt wysokopoziomowy to projekt architektury systemu, powinien być więc wykonany


przez architekta oprogramowania. Gdy funkcja architekta łączona jest z funkcją menedżera,
ten musi zatem posiadać odpowiednie doświadczenie techniczne.
Dekompozycja systemu powinna mieć charakter wysokopoziomowy i skupiać się na jego
funkcjonalności; pozostanie taka, aż do stworzenia stabilnego modelu analitycznego w fazie
analizy wymagań. Późniejsze jej modyfikacje są bardzo prawdopodobne wskutek między innymi
zidentyfikowania nowych podsystemów na etapie projektowania systemu (patrz rozdział 6.
„Projektowanie systemu — dekompozycja na podsystemy"), na co powinna być przygotowana
organizacja projektu. Architekt systemu identyfikuje więc jedynie główne podsystemy i ofero-
wane przez nie usługi, nie zajmując się nawet ich interfejsami.
14.4. Aktywności klasycznego zarządzania projektem 657

W fazie startowej projektu podsystemy będące wynikiem dekompozycji stanowią pod-


stawę dla jednostek organizacyjnych projektu: każdy podsystem przydzielony zostaje ze-
społowi, który staje się odpowiedzialny za jego zdefiniowanie i zrealizowanie. Zespoły pracujące
nad podsystemami zależnymi od siebie negocjują niezbędne usługi i interfejsy.

Dokonywanie podziału pracy

Istnieją różne podejścia do opracowywania struktury podziału pracy (WBS) dla danego
projektu. Najczęściej stosowana jest dekompozycja funkcjonalna bazująca na elementach
funkcjonalnych systemu. Przykładowa struktura WBS przedstawiona w tabeli 14.4 oparta jest
na jednostkach pracy dedykowanych poszczególnym aspektom funkcjonalnym bankomatu-
-wpłatomatu.

Tabela 14.4. Przykładowa struktura podziału pracy dla prostego oprogramowania bankomatu-wpłatomatu

Przypadek użycia Elementy struktury WBS


Uwi e r z y t e l ni ani e 1. Realizacja przypadku użycia Uwi e r z y t e l ni ani e
1.1. O p r a c o w a n i e f o r m u l a r z y interfejsu użytkownika
(dla logowania i zmiany PIN)
1.2. Realizacja uwierzytelniania na serwerze
1.3. Opracowanie tworzenia kont
Wypłata 2. Realizacja przypadku użycia opisującego wypłatę gotówki
2.1. Opracowanie formularzy interfejsu użytkownika
(dla wyboru konta i określenia kwoty)
2.2. Realizacja komunikacji z serwerem
2.3. Opracowanie logiki biznesowej dla akceptowania wypłaty
2.4. Opracowanie interfejsu dystrybutora nominałów

Wp?ata 3. Realizacja przypadku użycia opisującego wpłatę depozytu


3.1. Opracowanie formularzy interfejsu użytkownika (specyfikacja czeku,
wkładanie koperty)
3.2. Realizacja komunikacji z serwerem
3.3. Opracowanie logiki biznesowej dla rejestrowania depozytu
3.4. Opracowanie interfejsu dla drukarki potwierdzeń

Dekompozycja funkcjonalna może być także przydatna do definiowania zadań, co


wykorzystywane jest w projektach wysokiego ryzyka, kiedy to funkcjonalność systemu realizo-
wana jest przyrostowo w postaci zbioru prototypów. Jeżeli na przykład oprogramowanie oferuje
trzy główne funkcje, podział pracy powinien skutkować strukturą zadań niezbędną do tworze-
nia i dostarczania serii trzech prototypów.
Odmiennym podejściem jest oparcie podziału pracy na strukturze obiektów. Jeżeli na
przykład produkt składa się z pięciu komponentów, struktura WBS powinna obejmować pięć
zadań związanych z poszczególnymi komponentami. Podejście to jest najbardziej naturalne
w przypadku obiektowo zorientowanych projektów programistycznych. Przykład obiektowo
zorientowanego podziału pracy dla systemu składającego się z trzech komponentów widoczny
jest na rysunku 14.10.
658 Rozdział 12. • Zarządzanie racjonalizacją

Rysunek 14.10. Przykład obiektowo zorientowanego podziału pracy na trzy główne zadania dla
systemu dekomponowanego na trzy podsystemy

Jeszcze inne podejście do podziału pracy typowe jest dla projektu realizowanego przez
rozproszone grupy uczestników i bazuje na ich lokalizacjach geograficznych — dla każdej
z grup opracowywane są odrębne zadania. Pokrewnym podejściem jest podział pracy między
grupy wynikające ze struktury organizacyjnej firmy — dział marketingu, dział operacyjny, dział
sprzedaży — lub wręcz między poszczególnych programistów.
Niezależnie jednak od konkretnego podejścia, ostatecznym celem podziału pracy jest jej
dekompozycja na proste, wykonalne zadania, dla których można łatwo szacować czas trwania
i które można przyporządkowywać poszczególnym programistom 5 .
Dla menedżera dokonującego podziału pracy mogą być pomocne następujące heurystyki.

• Wykorzystuj istniejące struktury WBS. Skonsultuj się z menedżerami podobnych


projektów i wykorzystaj otrzymaną od nich informację.
• Korzystaj z pomocy kompetentnych programistów. Programiści posiadający wiedzę
z zakresu dziedziny problemowej powinni uczestniczyć w opracowywaniu struktury
podziału pracy. Ci, którzy dołączyli do projektu po jej opracowaniu, powinni mieć
możliwość krytycznego przejrzenia struktury przed rozpoczęciem wykonywania po-
wierzonych im zadań.
• Wyszukuj luki przydziału. Całość pracy wykonywanej w związku z projektem musi
być odwzorowana w zadania — każda aktywność musi być ostatecznie związana
z przynajmniej jednym zadaniem. Gdy pojawi się nowe zadanie, należy bądź to utwo-
rzyć dla niego nową aktywność, bądź dołączyć je do którejś z istniejących aktywności.

5
Z pracą programisty wiąże się ponadto wiele innych czynności, które muszą oni wykonywać i których
raczej nie uwzględnia się w strukturze WBS — przychodzenie do pracy, jedzenie i tym podobne —
i których statusu nie musi on zwykle raportować kierownikowi zespołu lub menedżerowi projektu.
Istnieją jednak menedżerowie próbujący ujmować tego typu czynności w formę oficjalnych zadań
— nazywamy ich powszechnie „mikromenedżerami".
14.4. Aktywności klasycznego zarządzania projektem 659

• Wyszukuj przypadki nakładania się pracy. Dane zadanie nie powinno wchodzić
w skład więcej niż jednej aktywności. Jeżeli przykładowo testowanie podsystemów
wykonywane jest przez oddzielny zespół testerów, menedżer projektu nie powinien
pozwalać na to, by wykonywali je także programiści.

Tworzenie początkowego harmonogramu

Znając czasowe zależności między zadaniami, możemy utworzyć harmonogram; wymaga


to jednak oszacowania długości trwania każdego z zadań, co jest dość trudnym zadaniem.
Oczywiście, oszacowanie to może opierać się na doświadczeniach z przeszłości, nie gwarantuje
jednak niezbędnej precyzji. Z konieczności musimy się więc najpierw zdać na harmonogram
początkowy, reprezentujący terminy uzgodnione między klientem a menedżerem projektu
— terminy na tyle generyczne, że możliwe do dotrzymania nawet w obliczu niespodziewa-
nych zmian.
Początkowo menedżer projektu powinien skonstruować harmonogram szczegółowy
jedynie dla pierwszego tygodnia realizacji projektu, uwzględniający między innymi zebranie
uruchamiające projekty, inicjacyjne spotkania w ramach poszczególnych zespołów oraz tuto-
riale; dla dalszego ciągu projektu harmonogram nie powinien być szczegółowy bardziej, niż
przewiduje to deklaracja problemu. Początkowa wersja SPMP powinna być potraktowana jako
propozycja przeznaczona do przejrzenia przez zespół architektoniczny oraz zespoły zajmujące
się poszczególnymi podsystemami, zanim stanie się wiążąca dla wszystkich uczestników
projektu.
Programiści każdego zespołu, zaraz po zakończeniu zebrania inicjacyjnego, powinni
przystąpić — na podstawie wstępnej dekompozycji systemu — do opracowywania „swego"
podsystemu, zgodnie ze strukturą WBS, która go dotyczy.
Opisane aktywności planistyczne muszą być realizowane przez zespół równolegle z analizą
pojawiających się wymagań oraz z aktywnościami składającymi się na projektowanie systemu.
Nowo rozpoznane zadania powinny zostać zaprezentowane w trakcie formalnego przeglądu
planistycznego, dokonywanego w fazie analizy wymagań lub tuż po jej zakończeniu. Efektem
tego przeglądu powinny być zrewidowana struktura WBS i zrewidowana architektura systemu.
Oba te produkty stanowią z kolei bazę dla drugiej wersji SPMP, która uzyskuje status linii
bazowej i jest podstawą uzgadnianej z klientem umowy projektu.
Sporządzanie harmonogramów wymaga ciągłego rozstrzygania kompromisów między
zasobami, czasem a zakresem implementowanej funkcjonalności, co spoczywa w gestii mene-
dżera projektu. Jest to zadanie bardzo skomplikowane zwłaszcza wtedy, gdy uczestnicy żon-
glować muszą udziałami w kilku projektach. Problematyka rozładowywania przeciążeń w wy-
korzystywaniu zasobów wykracza poza ramy tej książki.

14.4.2. Organizowanie projektu


W fazie startowej projektu menedżer dokonuje formowania zespołów zgodnie z wysokopo-
ziomowym projektem architektury systemu, ustanawia infrastrukturę komunikacyjną i uru-
chamia projekt, organizując dwa pierwsze zebrania. Gdy wszyscy uczestnicy otrzymają przydział
zadań i zbudowane zostaną mechanizmy raportowania statusu, można uznać, że projekt
wszedł w fazę ustaloną.
660 Rozdział 12. • Zarządzanie racjonalizacją

Aktywności startowe projektu, z jednej strony, mają krytyczne znaczenie dla jego jedno-
stek organizacyjnych, z drugiej, odbywają się pod silną presją czasu. Podstawowym problemem
może okazać się wówczas ta okoliczność, że poszczególni uczestnicy nie znają się dobrze i nie
mają jeszcze rozeznania w zakresie osobowości i stylów pracy kolegów. Zrozumienie rozmaito-
ści zakresów odpowiedzialności w ramach projektu to kolejne zagadnienie, którym powinien
zająć się menedżer.

Ustanowienie infrastruktury komunikacyjnej

Jasna i dokładna komunikacja jest czynnikiem krytycznym powodzenia projektu


zwłaszcza w sytuacji, gdy powiększa się liczba uczestników projektu. W konsekwencji zdefi-
niowanie i zbudowanie infrastruktury komunikacyjnej projektu powinno nastąpić możliwie
jak najwcześniej. Menedżer musi uwzględnić następujące tryby komunikacji.

• Tryby planowe związane są z zaplanowanymi „kamieniami milowymi" w postaci


przeglądów klienckich, przeglądów projektowych, zebrań statusowych zespołów, in-
spekcji i tym podobnych. Planowe tryby komunikacji stanowią formalne środki,
za pomocą których uczestnicy zbierają i upowszechniają informacje. Są one realizo-
wane głównie za pomocą środków komunikacji osobistej lub synchronicznej, takich
jak zebrania, formalne prezentacje, wideokonferencje i połączenia telekonferencyjne.
Dla każdej z tych interakcji menedżer projektu określa datę i desygnuje facilitatora.
• Tryby zdarzeniowe obejmują raportowanie problemów, żądania zmian, dyskutowanie
zagadnień i ich rozwiązań, a wynikają głównie z nieprzewidzianych problemów i kryzy-
sów. Realizowane są za pomocą środków asynchronicznych, takich jak e-mail, gro-
upware i bazy problemów, które powinny być udostępnione jak najwcześniej, w połą-
czeniu z ewentualnym przeszkoleniem z zakresu ich wykorzystywania. W przypadku
dużej liczby uczestników bardzo dobrze spisują się scentralizowane środki komuni-
kacji asynchronicznej w postaci witryn W W W i tablic ogłoszeń, są bowiem widoczne
dla większego grona osób niż e-mail czy dwustronne połączenia telefoniczne.

Iden tyfiko w anie umiejętności

Gdy tylko utworzona zostanie początkowa struktura WBS i modele zadań, menedżer
projektu ma już rozeznanie pod względem kwalifikacji niezbędnych do wykonania poszczegól-
nych zadań: do zbierania wymagań konieczna jest znajomość dziedziny aplikacyjnej, zadania
związane z dyskusjami wymagają umiejętności z zakresu komunikacji interpersonalnej, a te-
stowanie nie może się odbywać w oderwaniu od myślenia kategoriami jakościowymi, ukie-
runkowanego na szczegóły. Ogólnie rzecz biorąc, realizacja projektów programistycznych
wymaga szeregu rozmaitych umiejętności, między innymi:

• znajomości dziedziny aplikacyjnej, na przykład terminologii bankowej czy procedur


obliczania wysokości rat kredytów w kontekście aplikacji bankowych,
• zdolności komunikacyjnych, czyli umiejętności komunikacji z osobami nieznającymi
inżynierii oprogramowania lub dziedziny aplikacyjnej, a także umiejętności klarownego
formułowania skomplikowanych idei oraz osiągania konsensusu w negocjacjach,
661

oceny ryzyka na szczeblu projektowym, sprzężenia zwrotnego i — oczywiście —


motywacji do pracy.

ny (zawartej w CV), częściowo zaś na podstawie procedur organizacyjnych (wywiadów, te-

rodzajów zdolności: komunikacji, wczesnego rozpoznawania ryzyka i oddzielania decyzji me-


Rozdział 14. • Zarządzanie projektem
14.4. Aktywności klasycznego zarządzania projektem 663

• k a ż d y członek zespołu trzyosobowego m a szansę z a b r a n i a głosu, a e w e n t u a l n e r ó ż n i c e


z d a ń łatwo rozstrzygać; istnieje j e d n a k p r o b l e m z d o m i n o w a n i a j e d n e g o u c z e s t n i k a
przez p o z o s t a ł y m d w ó c h , p o n a d t o m a ł e zespoły cierpią n a n a d m i a r z a d a ń d o w y k o -
nania przez poszczególnych członków6,

• zespół czteroosobowy m o ż e spełniać swe f u n k c j e n a w e t w p r z y p a d k u niedyspozycji


j e d n e g o z u c z e s t n i k ó w ; parzysta liczba u c z e s t n i k ó w stwarza j e d n a k ryzyko r e m i s u
p r z y g ł o s o w a n i u , w s k u t e k c z e g o r o z w i ą z y w a n i e p r o b l e m ó w m o ż e p r z e c i ą g a ć się
w czasie,

• pięć l u b sześć t o najwłaściwsza liczba c z ł o n k ó w z e s p o ł u p r o g r a m i s t y c z n e g o ; człon-


k o w i e ci m o g ą w dalszym ciągu k o n t a k t o w a ć się b e z p o ś r e d n i o , p o n a d t o z r ó ż n i c o w a -
nie p u n k t ó w widzenia, p o t ę g o w a n e m i e s z a n i n ą r ó ż n y c h p o m y s ł ó w i r ó ż n y m nasta-
w i e n i e m , sprzyja k r e a t y w n e m u myśleniu; liczba c z ł o n k ó w jest wystarczająca d o tego,
b y przypisać i m w z a j e m n i e u z u p e ł n i a j ą c e się role, co czyni z zespołu r o d z a j m i n i o r -
ganizacji,

• siedmioro c z ł o n k ó w t o wciąż względnie e f e k t y w n a liczebność, j e d n a k ż e s k u t k u j ą c a


t e n d e n c j ą d o w y d ł u ż a n i a z e b r a ń (przegląd statusu z a j m u j e p ó ł g o d z i n y i dłużej); jeśli
faktycznie p o t r z e b n y jest taki zespół, zaleca się jego p o d z i a ł n a p o d z e s p o ł y z a j m u j ą c e
się częścią p r o b l e m ó w i z a g a d n i e ń d o o m ó w i e n i a ,

• ośmioro i więcej uczestników t o liczebność p o w o d u j ą c a w y r a ź n e t r u d n o ś c i w zarzą-


d z a n i u i sprzyjająca polaryzacji stanowisk: w czasie z e b r a n i a w y t w a r z a j ą się koalicje
p r o w a d z ą c e p r y w a t n e , p o b o c z n e konwersacje. W rezultacie zamiast w s p ó ł d z i a ł a n i a
m a m y d o czynienia z rywalizacją. I n a w e t jeśli ostateczne rezultaty z e b r a n i a są sa-
t y s f a k c j o n u j ą c e , z w y k l e u z y s k u j e się je w czasie d ł u ż s z y m n i ż w p r z y p a d k u m n i e j
licznych zespołów.

Formowanie zespołów

Kolejny k r o k w b u d o w a n i u organizacji p r o j e k t u to s f o r m o w a n i e zespołów, k t ó r e w y t w o -


rzą p r o d u k t y finalne. P i e r w s z y m e t a p e m f o r m o w a n i a z e s p o ł ó w jest u s t a n o w i e n i e ich k i e r o w -
n i k ó w p r z e z m e n e d ż e r a p r o j e k t u . K i e r o w n i k p r o j e k t u , o p r ó c z oczywistej u m i e j ę t n o ś c i r o z u -
m i e n i a statusu zespołu, p o w i n i e n p o s i a d a ć z d o l n o ś c i w zakresie e f e k t y w n e g o k o m u n i k o w a n i a
się, r o z p o z n a w a n i a n a d c h o d z ą c y c h k r y z y s ó w ( t e c h n i c z n y c h l u b m i ę d z y l u d z k i c h ) i rozstrzyga-
n i a k o m p r o m i s ó w , z g o d n i e z k r y t e r i a m i o k r e ś l o n y m i p r z e z cele p r o j e k t u .
Faza startowa p r o j e k t u s t a n o w i d o b r y m o m e n t n a p r z e s z k o l e n i e k i e r o w n i k ó w z e s p o ł ó w
i z a p o z n a n i e ich z p o d s t a w o w y m i r e g u ł a m i i p r o c e d u r a m i f u n k c j o n o w a n i a w r a m a c h p r o j e k t u .
N a l e ż y p r z y t y m o d r ó ż n i ć r o l ę k i e r o w n i k a z e s p o ł u o d roli l i d e r a t e c h n i c z n e g o : t e n o s t a t n i
— zwykle jest n i m łącznik z z e s p o ł e m a r c h i t e k t o n i c z n y m — współdziała z g ł ó w n y m architek-
tem oraz łącznikami architektonicznymi innych zespołów i m a ostatnie słowo w decyzjach
t e c h n i c z n y c h dotyczących zespołu. Jest oczywiste, że p e ł n i e n i e tej roli w y m a g a z n a k o m i t y c h
kwalifikacji technicznych i programistycznych.

6
Amerykanie nazywają to zjawisko „syndromem zbyt wielu kapeluszy" („too many hats" syndrome),
idiomatycznie u t o ż s a m i a j ą b o w i e m (noszony) kapelusz z rolą p e ł n i o n ą przez jego właściciela
— przyp. tłum.
664 Rozdział 12. • Zarządzanie racjonalizacją

W kontekście szkolenia, n a p r z y k ł a d k u r s ó w uniwersyteckich czy pilotażowego p r o j e k t u


p r z e m y s ł o w e g o e k s p l o r u j ą c e g o możliwości n o w y c h m e t o d i technologii, rolę k i e r o w n i k a ze-
s p o ł u m o ż n a podzielić n a d w i e role: k i e r o w n i k t r e n e r wyjaśnia i d o r a d z a zespołowi, f u n k c j o -
n u j ą c jednocześnie jako łącznik m i ę d z y zespołem a m e n e d ż e r e m projektu, n a t o m i a s t k i e r o w n i k
k u r s a n t w y p e ł n i a pozostałe zadania, takie j a k p r o w a d z e n i e z e b r a ń statusowych i przydzielanie
z a d a ń . W p r z y p a d k u k u r s ó w uniwersyteckich pierwszą z tych ról p e ł n i i n s t r u k t o r l u b d o ś w i a d -
c z o n y inżynier, p o d c z a s g d y d r u g a m o ż e być z p o w o d z e n i e m p e ł n i o n a p r z e z j e d n e g o z k u r -
santów. W projekcie f i r m o w y m rolę k i e r o w n i k a zespołu przydziela się p r o g r a m i ś c i e o b e z n a -
n e m u z p r o c e d u r a m i organizacyjnymi i kierowniczymi, a rolę łącznika architektonicznego
m o ż e pełnić programista posiadający wystarczające kwalifikacje techniczne.
P r z y d z i a ł p r o g r a m i s t ó w d o p r o j e k t u m o ż e n a s t ą p i ć j e d n o r a z o w o (tak z w a n a p ł a s k a
o b s a d a — f l a t staffing) l u b m o ż e b y ć d o k o n y w a n y s t o p n i o w o , w m i a r ę z w i ę k s z a j ą c y c h się
p o t r z e b k a d r o w y c h (tak z w a n a o b s a d a s t o p n i o w a n a — gradual staffing). Ten drugi wariant
p o d y k t o w a n y jest w z g l ę d a m i oszczędności zasobów: zbieranie w y m a g a ń nie w y m a g a t a k wielu
uczestników, co k o d o w a n i e i testowanie — swoją drogą, analityk i i m p l e m e n t a t o r t o role wy-
m a g a j ą c e r ó ż n y c h u m i e j ę t n o ś c i , więc p o w i n n y być p e ł n i o n e p r z e z r ó ż n e osoby. Płaska o b s a d a
m a j e d n a k tę zaletę, że wcześniej w y t w a r z a j ą się u k ł a d y m i ę d z y l u d z k i e n i e z b ę d n e d o efektywnej
i s p o n t a n i c z n e j k o m u n i k a c j i . W czasie g d y i m p l e m e n t a c j a i t e s t o w a n i e s y s t e m u są jeszcze
kwestią przyszłości, n i e k t ó r z y p r o g r a m i ś c i m o g ą uczestniczyć ( o b o k analityków) w analizie
w y m a g a ń , p o d c z a s g d y i n n i m o g ą zająć się c z y n n o ś c i a m i o r g a n i z a c y j n y m i (takimi jak b u d o -
w a n i e ś r o d o w i s k a z a r z ą d z a n i a k o n f i g u r a c j ą ) , eksploracją n o w y c h t e c h n o l o g i i czy s z k o l e n i e m
m n i e j d o ś w i a d c z o n y c h kolegów. Płaska o b s a d a o k a z u j e się lepsza w p r z y p a d k u k r ó t k i c h p r o -
j e k t ó w i k r ó t k i c h t e r m i n ó w dostarczania p r o d u k t ó w . W y b ó r m i ę d z y o b y d w o m a w a r i a n t a m i
o b s a d y d y s k u t o w a n y jest o b s z e r n i e w k s i ą ż c e F. P. B r o o k s a [Brooks, 1995].

Zebranie otwierające

M e n e d ż e r projektu, kierownicy zespołów i klient oficjalnie d o k o n u j ą u r u c h o m i e n i a


p r o j e k t u n a z e b r a n i u z u d z i a ł e m w s z y s t k i c h p r o g r a m i s t ó w . C e l e m t e g o z e b r a n i a jest p r z e -
kazanie uczestnikom informacji na temat zakresu projektu, jego infrastruktury k o m u n i k a -
c y j n e j i z a k r e s u o d p o w i e d z i a l n o ś c i k a ż d e g o z z e s p o ł ó w . P r e z e n t a c j a t a k a p o d z i e l o n a jest
między klienta i m e n e d ż e r a projektu: klient p r e z e n t u j e swe w y m a g a n i a i zakres projektu,
zaś m e n e d ż e r p r o j e k t u — i n f r a s t r u k t u r ę , w y s o k o p o z i o m o w y p r o j e k t s y s t e m u i o b o w i ą z k i
poszczególnych zespołów.

Podpisanie umowy projektu

Z a k r e s p r o j e k t u , jego czas t r w a n i a , koszt i p r o d u k t y f i n a l n e t o a t r y b u t y p r o j e k t u o k r e -


ślane w s p o s ó b f o r m a l n y w p o s t a c i umowy projektu (Project Agreement). U m o w a ta m o ż e
m i e ć f o r m ę k o n t r a k t u , b i z n e s p l a n u l u b k a r t y i n f o r m a c y j n e j p r o j e k t u 7 . F i n a l i z o w a n a jest
z w y k l e p o u s t a b i l i z o w a n i u się m o d e l u a n a l i t y c z n e g o , g d y p l a n y całej r e s z t y p r o j e k t u są j u ż
z a a w a n s o w a n e , i z a w i e r a ć p o w i n n a co n a j m n i e j n a s t ę p u j ą c e e l e m e n t y :

7
Zwanej też popularnie c z a r t e r e m p r o j e k t u (project charter) — przyp. tłum.
i • listę dokumentów dost9.rcz3.nych klientowi,

• kryteria akceptacji systemu.

14.43. Kontrolowanie projektu

By menedżer mógł podejmować decyzje w sposób efektywny, musi dysponować adekwatną

być informacja o statusie poszczególnych jego komponentów i wpływie ich stanu na stan

zane drobne problemy, które nie mają większego wpływu na harmonogram, po skumulowaniu

rygujących. Informacja ta może być jednak nierzetelna, jeśli członkowie zespołu


nie współpracują ze swym kierownikiem. Ich opór przed zgłaszaniem problemów
wynika głównie z obawy o nadmierną ingerencję menedżera projektu w ich pracę,

milowych" {milestones) Menedżerowie mogą zwiększać dokładność pomiaru owego


dobrze może być naszpikowany całą gamą błędów poważnych. Podobne zastrzeżenia
666 Rozdział 12. • Zarządzanie racjonalizacją

można mieć do „zakończenia" innych faz — testowania, demonstrowania cech,


dokumentowania i integrowania. Definiowanie i monitorowanie „ostrych" kamieni
milowych nie wymaga od menedżera konsultacji z programistami.
• Przeglądy projektu. Przeglądy projektu są środkiem dla wymiany informacji statuso-
wej na forum wszystkich uczestników, w postaci formalnych prezentacji. Cierpią one
na tę samą przypadłość, co zebrania statusowe — opór uczestników przed zgłasza-
niem problemów.
• Inspekcje kodu. Formalne, partnerskie przeglądy kodu stanowią doskonałą technikę
wczesnego wykrywania usterek. Gdy przeprowadzane są często i regularnie, ich
rezultaty są dobrą miarą postępu w projekcie.
• Demonstrowanie prototypów. Prototypy stanowią częściowe implementacje systemu,
przeznaczone dla ewaluacji określonej technologii lub funkcjonalności. Ponieważ
ignorują takie zagadnienia jak przypadki graniczne czy niezawodność, są jedynie
przydatne dla początkowego pomiaru postępu, lecz nie kwalifikują się na „ostre"
kamienie milowe w kategoriach zakończenia zadania.

Metryki

Nie sposób efektywnie zarządzać ewolucją produktu bez obiektywnych mierników jego
stanu. Chociaż zebrania i inspekcje pomagają w jakościowej ocenie statusu projektu, opierają
się na przesłankach subiektywnych, czyli percepcji wybranych uczestników i ich chęci do
dzielenia się wiedzą: w efekcie nadmierny optymizm pojedynczego uczestnika może stworzyć
wrażenie, że projekt jest bardziej zaawansowany, niż ma to miejsce w rzeczywistości.
Jedną z grup mierników ilościowych są metryki programistyczne procesu lub systemu.
Instrumentacja mająca na celu automatyzację zbierania tych metryk sprzyja ich obiektywi-
zmowi. Zaproponowano wiele rodzajów takich metryk, opartych między innymi na liczbie
wierszy kodu źródłowego, punktów rozgałęzień i rozwidleń (o czym pisze T. McCabe
[McCabe, 1976]), liczbie zmiennych i operatorów (opisanych w pracy M. H. Halsteada
[Halstead, 1977]) i liczbie wymagań funkcyjnych, o czym piszą A. J. Albrecht i J. E. Gaffney Jr.
[Albrecht i Gaffney, 1983].
Metryki programistyczne sprawdzają się średnio jako narzędzie menedżerskie z tej
prostej przyczyny, że wiele produktów wytwarzanych w ramach projektu nie wiąże się z pro-
gramowaniem sensu stricte, zatem uzyskiwane metryki czysto programistyczne wymagają
odpowiedniej interpretacji przez menedżera. Przykładowo gwałtownie eksplodująca liczba
wierszy kodu źródłowego może, co prawda, świadczyć o postępie w implementowaniu
podsystemu, ale równie dobrze może być wynikiem sztucznego duplikowania kodu zamiast
jego strukturalizacji8. W przypadku jednak podejścia iteratywnego lub podejścia ze scentrali-

8
Pamiętajmy, że znakomita większość języków programowania stwarza szeroką dowolność w zakresie
podziału kodu między wiersze — jedynym bodaj wyjątkiem w tym względzie są języki asemblerowe —
łatwo zatem sztucznie proliferować licznik wierszy kodu, nie wnosząc doń żadnych nowych elementów.
Bardziej obiektywnym miernikiem mógłby być licznik instrukcji w kodzie źródłowym — mógłby, gdyby
nie możliwość zapisywania złożonych obliczeń zarówno w formie złożonych wyrażeń ewaluowanych
w ramach jednej instrukcji, jak i w formie pojedynczych instrukcji ograniczających się do wartościowania
pojedynczych operatorów. Obie metryki — liczba wierszy i liczba instrukcji — mają zatem sens jedynie
w przypadku rozsądnego i spójnego w dłuższej perspektywie stylu kodowania — przyp. tłum.
14.4. Aktywności klasycznego zarządzania projektem 667

z o w a n ą a r c h i t e k t u r ą k o m p o n e n t y p r o g r a m o w e są ł a t w i e j w e r y f i k o w a l n e i m i e r z a l n e . C o
więcej, m e t r y k i m o g ą w ó w c z a s m i e r z y ć a t r y b u t y p r o c e s u , t a k i e j a k o d s t ę p s t w a o d h a r m o -
n o g r a m u , zużycie z a s o b ó w czy liczba u c z e s t n i k ó w r e z y g n u j ą c a z u d z i a ł u w p r o j e k c i e p r z e d
jego zakończeniem.

• Miara kondycji finansowej projektu. P o r ó w n a n i e rzeczywistego kosztu realizacji p r o -


jektu w d a n y m m o m e n c i e ( b ę d ą c y m n a p r z y k ł a d „ k a m i e n i e m m i l o w y m " ) z k o s z t e m
p l a n o w a n y m n a t e n m o m e n t stanowi dla m e n e d ż e r a p e w n ą m i a r ę k o n d y c j i f i n a n s o -
w e j p r o j e k t u . O w a m i a r a j e s t j e d n a k t y l k o c z ę ś c i ą p r a w d y ; f a k t , że z u ż y t o m n i e j
z a s o b ó w niż p l a n o w a n o , m o ż e w y n i k a ć z o s z c z ę d n e g o g o s p o d a r o w a n i a t y m i zaso-
b a m i , r ó w n i e d o b r z e j e d n a k m o ż e być k o n s e k w e n c j ą faktu, że w y k o n a n o m n i e j zadań,
niż wynika to z planowanego na obecną chwilę kosztu — absolutna bezczynność,
s k u t k u j ą c a z e r o w y m k o s z t e m rzeczywistym, stanowiłaby w t y m kontekście m a k s y -
m a l n ą oszczędność! Niższy o d p l a n o w a n e g o koszt n i e k o n i e c z n i e m u s i z a t e m b y ć
p o w o d e m d o z a d o w o l e n i a , m o ż e być b o w i e m ś w i a d e c t w e m o p ó ź n i e ń w projekcie.
A b y więc o p i s y w a n a m i a r a k o n d y c j i f i n a n s o w e j stała się obiektywna, należy w z b o g a -
cić ją o trzeci w s k a ź n i k — wartość w y p r a c o w a n ą (earned value) — stanowiący s u m ę
p l a n o w a n e g o k o s z t u dla wszystkich z a d a ń wykonanych do tej pory. N a r y s u n k u 14.11
cienką linią z a z n a c z o n o koszt p l a n o w a n y , g r u b ą koszt rzeczywisty, zaś p r z e r y w a n ą
— wartość w y p r a c o w a n ą . Jak widzimy, w a r t o ś ć w y p r a c o w a n a jest obecnie m n i e j s z a
niż p l a n o w a n y koszt — oznacza t o ni m n i e j , nie więcej tylko o p ó ź n i e n i e w realizacji
projektu; koszt rzeczywisty plasuje się j e d n a k p o n i ż e j wartości w y p r a c o w a n e j , a t o
oznacza, że p r o j e k t mieści się w budżecie — s u m a r y c z n y koszt w y k o n a n y c h d o tej
p o r y z a d a ń jest m n i e j s z y n i ż p l a n o w a n y . W projekcie r e a l i z o w a n y m w idealnej zgo-
dzie z h a r m o n o g r a m e m i b u d ż e t e m wszystkie trzy k r z y w e — k o s z t u p l a n o w a n e g o ,
kosztu rzeczywistego i wartości w y p r a c o w a n e j — p o k r y w a j ą się. Sytuacja, w k t ó r e j
k r z y w a w a r t o ś c i w y p r a c o w a n e j g ó r u j e n a d p o z o s t a ł y m i d w i e m a , jest p o w o d e m d o
o p t y m i z m u (niezależnie o d relacji m i ę d z y k o s z t e m rzeczywistym a p l a n o w a n y m ) .

Rysunek 14.11. Oszacowanie kondycji finansowej projektu przy użyciu wartości wypracowanej. Koszt
rzeczywisty jest mniejszy niż wartość wypracowana, co oznacza faktyczne oszczędności; wartość wypra-
cowana jest jednak mniejsza od planowanej na daną chwilę, zatem realizacja projektu jest opóźniona
Rozdział 14. • Zarządzanie projektem

zatem na przykład liczbę realizowanych żądań zmiany należy za zrealizowane"


uważać tylko te zmiany, dla których testy regresyjne nie wykryły nowych defektów.

czenia) czas ten powinien być coraz dłuższy, co świadczyłoby o zwiększającej się
14.4. Aktywności klasycznego zarządzania projektem 669

Zarządzanie ryzykiem

Istotą zarządzania ryzykiem jest identyfikacja możliwych problemów i próba ich unik-
nięcia, zanim staną się zagrożeniem dla budżetu czy harmonogramu. Kluczowym elementem
zarządzania ryzykiem jest zapewnienie odpowiedniego przepływu informacji, tak by ryzyko
i problemy były raportowane dokładnie i na bieżąco. Wiele projektów załamuje się dlatego, że
nawet proste problemy zgłaszane są zbyt późno, bądź dlatego, że podejmuje się próbę rozwią-
zywania niewłaściwego problemu. W tej sekcji skupimy się na aktywnościach związanych
z identyfikowaniem możliwych konsekwencji ryzyka, analizowaniem ich i reagowaniem na
nie. Czytelnikom zainteresowanym szczegółami zarządzania ryzykiem w kontekście inżynierii
oprogramowania polecamy specjalistyczną literaturę o tej tematyce, między innymi publikację
B. Boehma [Boehm, 1991] i książkę R. N. Charetta [Charette, 1989].
Pierwszym krokiem w zarządzaniu ryzykiem jest jego identyfikacja: ryzyko potencjal-
nych problemów wynika z obszarów niepewności w projekcie. Niepewność ta może mieć cha-
rakter menedżerski lub techniczny: w pierwszym przypadku odnosi się między innymi do
organizacji, produktów, ról czy planu zadań, w drugim do modeli systemu, jego funkcjonalno-
ści, architektury i implementacji. Szczególnie groźnym ryzykiem o charakterze technicznym są
błędy wykrywane w późnych stadiach realizacji projektu. W tabeli 14.5 przedstawiliśmy przy-
kłady ryzyka związane z projektem OWL, opisywanym między innymi w sekcji 3.4.1.

Tabela 14.5. Przykładowe elementy ryzyka

Ryzyko Typ

Zachowanie komercyjnych dokumentów „z półki" (Components-Ojf-The-Shelf Techniczne


w skrócie COTS) będzie niezgodne z opublikowanymi standardami.
Dostarczenie komponentów COTS odbędzie się później niż zaplanowano. Menedżerskie

Użytkownikom nie przypadnie do gustu wybrany interfejs interakcji z systemem. Techniczne

Wybrane middleware będzie zbyt wolne dla sprostania wymogom efektywności Techniczne
rejestrowania danych.
Opracowywanie podsystemów zajmie więcej czasu niż zaplanowano. Menedżerskie

Programiści świadomi są zwykle ryzyka związanego z wykonywanymi zadaniami; sęk


w tym, żeby zachęcić ich do raportowania tej wiedzy, by skutecznie stawić czoła temu ryzyku.
Spontaniczne raportowanie świadomości ryzyka nie jest jednak wystarczające, bowiem uczest-
nicy projektu — klient, programiści i ich kierownicy — często nie doceniają elementu ryzyka
lub traktują go zbyt optymistycznie. Niezbędne są zatem bardziej formalne metody identyfi-
kowania ryzyka, na przykład zbieranie związanych z nim informacji za pomocą strukturalnych
kwestionariuszy; poszczególni uczestnicy odpytywani są na okoliczność ryzyka, jakiego świa-
domi są w związku z wykonywanymi przez siebie zadaniami. W widocznym poniżej przykładzie,
zaczerpniętym z pracy M. J. Carra, S. L. Kondy, I. Monarcha, F. C. Ulricha i C. F. Walkera [Carr
i in., 1993], uczestnicy proszeni są o odpowiedź na pytania związane z wymaganiami poza-
funkcyjnymi, sformułowanie zgodnie ze schematem taksonomicznej identyfikacji ryzyka
(Taxonomy-Based Risk Identification): zależnie od odpowiedzi na pierwsze pytanie, ankieter
670 Rozdział 12. • Zarządzanie racjonalizacją

zadaje następne pytania, mające na celu wykluczenie pokrewnych elementów ryzyka. Racjona-
lizacją takiego kwestionariusza jest eksploracja wszystkich obszarów projektu, w których kryć
się może potencjalne ryzyko.

1. Wymagania

d) wydajność

[23] Czy wykonano analizę wydajności?


(Tak) (23.a) Jaki jest stopień twojego zaufania do wyników p r z e p r o w a d z o n e j
analizy wydajności?
(Tak) (23.b) Czy dysponujesz modelem badania wydajności w trakcie projektowania
i implementowania systemu?

Lista zidentyfikowanych elementów ryzyka może być dość pokaźna, choć poszczególne
jej pozycje mogą mieć zróżnicowane znaczenie — od błahych, pomijalnych lub wręcz nie-
prawdopodobnych do krytycznych dla projektu; na tych ostatnich powinien się w pierwszym
rzędzie skupiać menedżer projektu, co wymaga opracowania systemu ich priorytetyzacji.
Bazując na dwóch najważniejszych atrybutach czynników ryzyka — prawdopodobieństwie
zaistnienia i potencjalnym wpływie na realizację projektu — czynniki te podzielić możemy
na cztery następujące kategorie:

• prawdopodobne, poważnie zagrażające projektowi,


• mało prawdopodobne, lecz równie poważnie zagrażające projektowi w razie zaistnienia,
• prawdopodobne, stanowiące niewielkie zagrożenie dla projektu,
• mało prawdopodobne i nawet w razie zaistnienia w niewielkim stopniu zagrażające
projektowi.

Jest zrozumiałe, że menedżerowie powinni obawiać się przede wszystkim czynników


ryzyka pierwszej z wymienionych kategorii. Konkretnym wyrazem tej obawy powinno być
monitorowanie poziomu ryzyka i opracowanie planów awaryjnych na wypadek, gdy po-
ziom ten stanie się wystarczająco wysoki. Menedżerowie nie powinni także ignorować
czynników ryzyka drugiej kategorii, choć związane z nimi plany awaryjne mają rację bytu
dopiero wówczas, gdy prawdopodobieństwo ich zaistnienia znacząco się zwiększy. Czynniki
ryzyka należące do dwóch ostatnich kategorii nie powinny wzbudzać obaw menedżerów
i mogą być przez nich zignorowane, chyba że potencjał dostępnych zasobów zezwala na ich
monitorowanie. W tabeli 14.6 możemy zobaczyć, jak czynniki ryzyka przedstawione w tabeli
14.5 mają się do opisanej klasyfikacji.
Gdy uczestnicy z menedżerem na czele uświadomią sobie poszczególne czynniki ryzyka
i ich istotność, powinni opracować strategie zarówno zmniejszające prawdopodobieństwo ich
zaistnienia, jak i łagodzenia ich potencjalnego wpływu na projekt. Ryzyko jest zwykle konse-
kwencją niepewności, spowodowanej bądź to zagubieniem informacji, bądź też opieraniem
się na informacjach niewiarygodnych. Jeżeli zatem na przykład chodzi o komponenty „z półki",
14.4. Aktywności klasycznego zarządzania projektem 671

Tabela 14.6. Priorytetyzacja przykładowych czynników ryzyka

Ryzyko Prawdopodobieństwo Potencjalne zagrożenie


Zachowanie k o m e r c y j n y c h Mało prawdopodobne. Poważne
d o k u m e n t ó w „z półki"
(Components-Off-The-Shelf\
w skrócie COTS) będzie niezgodne
z opublikowanymi standardami.

Dostarczenie k o m p o n e n t ó w COTS Prawdopodobne. Poważne


odbędzie się później niż zaplanowano.

U ż y t k o w n i k o m nie p r z y p a d n i e Prawdopodobne ze strony Poważne


do gustu wybrany interfejs interakcji użytkowników spędzających
z systemem. więcej niż dwie godziny
dziennie przy pracy
z komputerem.

W y b r a n e middleware będzie zbyt Mało p r a w d o p o d o b n e Poważne


wolne dla sprostania wymogom ze względu na małą
efektywności rejestrowania danych. częstotliwość próbkowania.

Opracowywanie podsystemów zajmie Prawdopodobne, już raz Poważne


więcej czasu niż zaplanowano. dało o sobie znać.

programiści mogą minimalizować związane z nimi ryzyko, zmieniając ich dostawcę na bardziej
wiarygodnego bądź też — redundantnie — korzystać z oferty kilku dostawców9. W większości
przypadków jednak minimalizowanie ryzyka wiąże się z dodatkowymi kosztami i dodatko-
wym zapotrzebowaniem na zasoby. W tabeli 14.7 mogą czytelnicy zobaczyć, jak można mini-
malizować czynniki ryzyka wymienione w tabeli 14.5.
Gdy opracowane zostaną już plany minimalizowania ryzyka, należy przekazać je wszyst-
kim zainteresowanym, przy użyciu tych samych kanałów, którymi komunikowane są doku-
menty techniczne; dzięki temu uczestnicy mogą skojarzyć dokumentację techniczną z to-
warzyszącymi im czynnikami ryzyka. Skuteczność zarządzania ryzykiem uwarunkowana jest
terminową komunikacją, należy więc zachęcać programistów do bieżącego raportowania
potencjalnych problemów. Wielokrotnie przecież pisaliśmy o tym, że podstawową barierą
w zmaganiach z niepewnością jest nieefektywna komunikacja.

14.4.4. Kończenie projektu


W fazie końcowej projektu menedżer czyni przygotowania do testów akceptacyjnych, nadzo-
ruje integrowanie systemu i jego testowanie oraz uczestniczy w instalowaniu systemu w śro-
dowisku klienta, a po zakończeniu tych wszystkich czynności przystępuje, wraz z innymi
uczestnikami, do podsumowania zakończonego projektu w formie sesji post mortem.

9
Wiarygodni dostawcy, świadomi myślenia programistów kategoriami ryzyka, umożliwiają darmowe
testowanie swych produktów w konkretnym zastosowaniu, aż do (spodziewanego) komercyjnego
usankcjonowania tego zastosowania, które — oczywiście — wymaga zakupu licencji — p r z y p . tłum.
672 Rozdział 12. • Zarządzanie racjonalizacją

Tabela 14.7. Przykładowe strategie minimalizowania ryzyka

Ryzyko Strategie minimalizowania


Zachowanie komercyjnych d o k u m e n t ó w • Wykonanie benchmarków identyfikujących
„z półki" (Components-Off-The-Shelf-Components,• niezgodności.
w skrócie COTS) będzie niezgodne Przeanalizowanie możliwości ominięcia
z o p u b l i k o w a n y m i standardami. problematycznych funkcji.

Dostarczenie k o m p o n e n t ó w COTS odbędzie • Monitorowanie ryzyka opóźnienia, poprzez


się później niż zaplanowano. zażądanie od dostawcy m o n i t o r o w a n i a
postępu prac.

Użytkownikom nie przypadnie do gustu • Przeprowadzenie analiz użyteczności w oparciu


wybrany interfejs interakcji z systemem. • o makiety.
Opracowanie alternatywnego interfejsu.

W y b r a n e middleware będzie zbyt wolne dla • Monitorowanie ryzyka, zaplanowanie


sprostania wymogom efektywności rejestrowania prototypów ewaluujących efektywność.
danych.

Opracowywanie podsystemów zajmie więcej • Zwiększenie priorytetu implementacji


czasu niż zaplanowano. problematycznego podsystemu w stosunku
do innych podsystemów.

Akceptacja systemu

Celem klienckich testów akceptacyjnych jest przekonanie klienta, za pomocą sto-


sownych prezentacji, że przekazywany mu system spełnia kryteria akceptacyjne określone
w umowie projektu. Ostatecznie klient przyjmuje system albo odmawia jego przyjęcia z powo-
du niespełnienia wspomnianych kryteriów; przyjęcie systemu jest widocznym aktem za-
kończenia projektu.
Testy akceptacyjne przeprowadzane są w formie serii prezentacji obrazujących elementy
funkcjonalne systemu i inne interesujące cechy. Szczególnie scenariusze wymienione w umo-
wie projektu demonstrowane są przez programistów lub przyszłych użytkowników. Prezentacji
podlegają także cechy pozafunkcyjne systemu — dokładność, niezawodność, reaktywność
i bezpieczeństwo. W sytuacji gdy testy akceptacyjne poprzedzone już zostały zainstalowaniem
systemu i jego oceną przez użytkowników, następuje zaprezentowanie tej oceny. Testy akcepta-
cyjne stanowią ponadto okazję do dyskusji na temat dalszych działań związanych z systemem,
między innymi jego konserwacji, transferu wiedzy czy też przyszłych jego rozszerzeń.

Instalowanie systemu

Zainstalowanie systemu jest pierwszym krokiem do przeprowadzania testów „polowych",


porównywania rezultatów jego pracy z wykazywanymi przez jego dotychczas eksploatowany
odpowiednik oraz szkolenia użytkowników. Zależnie od ustaleń w umowie projektu, instalację
systemu przeprowadza dostawca, podwykonawca albo sam klient.
673

W celu zminimalizowania ryzyka po stronie klienta instalacja systemu i jego „polowe"


testowanie odbywają się początkowo w „próbnym" środowisku, oddzielonym od danych eks-
ploatacyjnych, w sposób przyrostowy. Dopiero wówczas, gdy klient zyska przekonanie,
iż wprowadzenie nowego systemu do eksploatacji nie spowoduje większych problemów
w funkcjonowaniu firmy, system ten w chodzi w pełną fazę eksploatacyjną. Ponieważ po-
czątki eksploatacji nowego systemu zawsze wiążą się z jakimiś problemami, rzadko klient
decyduje się od razu na pełne wdrożenie systemu.

klientowi, są również wnioski i lekcje, jakie wynoszą z tego projektu jego uczestnicy, ku (inten-
cjonalnemu) pożytkowi w przyszłych projektach. Wiele firm programistycznych ujmuje tę
oczywistą skądinąd prawdę w formalne ramy swoistej analizy, zwanej popularnie analizą post

terminu dostarczenia produktu z terminem planowanym, liczba i rodzaj wykrytych usterek


wykrywanych w czasie testowania produktu, techniczne i menedżerskie problemy napotkane
w czasie jego realizowania i szeroko rozumiane sugestie na przyszłość. Mimo iż w cyklu życio-
wym projektu faza post mortem jest fazą najmniej spektakularną, ma istotne znaczenie dla firmy
w kontekście realizowanych przez nią następnych projektów.

14.5. Aktywności „zwinnej" realizacji projektu

Opisywane dotychczas metody zarządzania projektami stanowią owoc wieloletnich doświad-

planowania i podejmowania decyzji. Metodologie te doskonale sprawdzają się przy dobrze

Wobec naturalnego zapotrzebowania na sposoby skuteczniej szego stawiania czoła takim

zmiany architektury systemu powodowane adaptacją (lub odrzucaniem) określonych tech-


nologii, nowe platformy sprzętowe, nowe maszyny wirtualne — temu wszystkiemu nie są
w stanie sprostać klasyczne podejścia, traktujące wszelkie zmiany jako zło konieczne, które
można co najwyżej kontrolować i które postrzegane jest jak zamach na klarowną, czytelną
i dążącą do doskonałości strukturę projektu.
„Zwinne" metody zrywają z monolitem długiego dystansu na rzecz tworzenia produktu
w postaci małych przyrostów, realizowanych w krótkich odcinkach czasu. Po zrealizowaniu
każdego przyrostu analizowany jest post factum postęp jego realizacji, wnioski wypływające
674 Rozdział 12. • Zarządzanie racjonalizacją

W tej sekcji opiszemy krótko istotę „zwinnej" technologii Serum (omówionej o pracy
K. Schwabera i M. Beedlea [Schwaber i Beedle, 2002]) jako kontrast do metodologii opisywa-
nych w poprzednich sekcjach. Zauważmy przy tym, że wiele aktywności opisywanych w po-
przednich sekcjach, związanych między innymi z zarządzaniem kwalifikacjami, zarządzaniem
ryzykiem i instalowaniem systemu, zachowuje swą aktualność także na gruncie metodologii
Serum.

14.5.1. Planowanie projektu: wykazy zaległości produktu i przebiegu


Metodologia Serum charakteryzuje się krótkimi iteracjami, noszącymi nazwę przebiegów
(sprint), a ramach których odbywają się kolejne akty doskonalenia produktu, przeznaczone do
kolejnych emisji; kandydatury do tych emisji nazywane są potencjalnie dostarczalnymi
przyrostami produktu (potentially deliverable product increment). W konsekwencji Scrum jest
przykładem integracji pionowej (patrz sekcja 11.4.4).
Projekt uruchomiony zostaje na zebraniu startowym (project kick off meeting), w ra-
mach którego właściciel produktu i menedżer (zwany w terminologii Serum „mistrzem")
ustalają (w ramach burzy mózgów) pierwszą wersję wymagań. Po zakończeniu zebrania
wymagania te układane są w formę listy priorytetowej, zwanej wykazem zaległości pro-
duktu (product backlog). Lista ta ewoluuje od postaci początkowej, sformułowanej w kate-
goriach wymagań biznesowych, i w miarę postępu w realizacji systemu aktualizowana jest
w kategoriach wymagań systemowych. Na bieżąco odzwierciedla więc ona rozumienie pro-
duktu przez klienta i ekipę projektu, stanowiąc w ten sposób „zwinny" wariant klasycznego
kontraktu, formułującego w sposób ustalony a priori wymagania dotyczące wszystkich pro-
duktów finalnych.
Każdy przebieg rozpoczyna się od zebrania planistycznego, w ramach którego uczestnicy
(klient, menedżer i zespół) wybierają z wykazu zaległości produktu elementy składające się na
kolejny przyrost. Następnie zespół określa zbiór zadań realizujących ten przyrost, szacując
jednocześnie związany z tym wysiłek, i formułuje je w formie listy zwanej wykazem zaległości
przebiegu (sprint backlog), stanowiącej swego rodzaju zobowiązanie do dostarczenia określo-
nego produktu na końcu przebiegu. Czas trwania przebiegu wynosi zazwyczaj 30 dni. Po jego
zakończeniu efekt poddawany jest przeglądowi i następuje zdefiniowanie zakresu następnego
przebiegu. Wszelkie niezrealizowane i niedokończone zadania wracają do wykazu zaległości
produktu.
Pozycje wykazu zaległości produktu pogrupowane są względem kolejnych emisji z okre-
ślonymi datami. Po zakończeniu każdego z przebiegów właściciel produktu ma do wyboru
trzy opcje:

1. zadecydowanie, czy rezultat przebiegu kwalifikuje się jako przyrost produktu


finalnego,
2. zadeklarowanie zakończenia projektu i dostarczenie kompletnego produktu
finalnego,
3. skorygowanie planu, przez zmianę dat poszczególnych emisji, stosownie do dotych-
czasowych postępów zespołu.
14.5. Aktywności „zwinnej" realizacji projektu 675

14.5.2. Organizowanie projektu


Metodologia Serum definiuje trzy role. Oto one.

• Właściciel produktu (product owner), odpowiedzialny za wymagania. Partycypuje


w tworzeniu wykazu zaległości produktu i jego priorytetyzacji przed kolejnymi
przebiegami. Właściciel produktu jest reprezentantem klienta i użytkowników, w dys-
kusjach dysponuje jednym głosem. Jest wyłącznie odpowiedzialny za wykaz zale-
głości produktu, lecz nie może ingerować w pracę zespołu realizującego przebieg.
• Mistrz (Serum master), zwany żargonowo „młynarzem" 10 , jest de facto menedżerem
odpowiedzialnym za proces. Przydziela role i wymusza ich właściwą realizację, pro-
wadzi codzienne zebrania statusowe i stara się likwidować przeszkody uniemożli-
wiające lub utrudniające programistom ich normalną pracę. Pełni funkcję swoistego
interfejsu między właścicielem produktu a zespołem, zapewniając jego produktyw-
ność i chroniąc przed zakłóceniami.
• Zespół (Scrum team) odpowiedzialny jest za tworzenie kolejnych przyrostów. Ma
charakter międzyfunkcyjny — jego członkowie posiadają kwalifikacje niezbędne do
realizacji przyrostu. Zespół jest samoorganizujący się w tym sensie, że każdy wyko-
nuje pracę, na którą pozwalają mu kwalifikacje i dostępny czas; nie ma wydzielonych
ról analityka, implementatora czy testera. Wykonywana praca stanowi jednocześnie
okazję do nauki i zbierania nowych doświadczeń dla każdego programisty. Zespół
uczestniczy w zebraniu planistycznym przebiegu, przyjmując zobowiązanie do zreali-
zowania wykazu zaległości przebiegu.

14.5.3. Kontrolowanie projektu: dni robocze i wykresy wygaszania


Mistrz monitoruje przebieg realizacji projektu, zwołując codzienne zebrania statusowe, spo-
rządzając i aktualizując wykres wygaszania przebiegu. Zebrania takie mają miejsce na początku
każdego dnia roboczego i trwają nie dłużej niż 15 minut. W ramach zebrania każdy zespół składa
indywidualnie raport na temat:

• statusu, czyli pracy wykonanej od ostatniego zebrania,


• problemów, które stanęły na przeszkodzie wykonaniu zaplanowanej pracy bądź
utrudniły jej wykonanie,
• nowych elementów działania, czyli pracy zaplanowanej na bieżący dzień.

Uzyskana w ten sposób informacja umożliwia mistrzowi monitorowanie postępu każdego


zespołu i podejmowanie ewentualnych akcji korygujących w postaci:

• planowania nowej pracy, nieuwzględnionej w ramach zebrania planistycznego prze-


biegu lub poprzedniego zebrania statusowego,

10
Angielskie Serum oznacza dosłownie „młyn" i zaczerpnięte jest z terminologii rugby — patrz na przy-
kład http://pl.wikipedia.org/wiki/Scrum — przyp. tłum.
676

wyłącznie osoby zainteresowane danym zagadnieniem. Oszczędza to maksymalnie czas pro-

kle bardzo szybko.

przebiegu. Aby można było kontrolować postęp przebiegu i oceniać pojawiające się w nim
14.6. Literatura uzupełniająca 677

14.5.4. Kończenie projektu: przeglądy przebiegów


Choć postęp w pracy zespołów Serum widoczny jest na bieżąco, dzięki codziennym zebraniom,
właściciel produktu nie może ingerować w ich pracę, a rola mistrza ogranicza się do usuwania
przeszkód utrudniających tę pracę. Dopiero po zakończeniu przebiegu mistrz może zmienić
skład zespołów, a właściciel projektu zmodyfikować swe wymagania na bazie dotychczasowej
wydajności programistów. Jeśli jednak w trakcie realizacji przebiegu okaże się, że jego cele
okazują się nie do zrealizowania lub stały się nieaktualne, zarówno zespół, jak i mistrz mogą
przedwcześnie zakończyć („skasować") przebieg. Jako że przebiegi są raczej krótkoterminowe,
tego typu sytuacje powinny należeć do rzadkości.
Po zakończeniu każdego przebiegu właściciel produktu może renegocjować z klientem,
którego reprezentuje, zawartość wykazu zaległości produktu. Bazując na ostatnim „przyroście"
produktu, może zaproponować zmianę niektórych wymagań lub zrewidowanie ich priorytetu;
bazując na obserwowanej wydajności zespołów, może wnioskować o zmianę terminów emisji
lub zmianę przydziału zasobów.
Na rysunku 14.13 przedstawiono schemat metodologii Serum w postaci diagramu ak-
tywności UML.

14.6. Literatura uzupełniająca


Dobry przegląd generycznych koncepcji zarządzania projektami zawarty jest w książce S. E.
Portny ego [Portny, 2001]. Zespoły stanowią kluczowy element wielu organizacji projektu;
w pracy J. R. Katzenbacha i D. K. Smitha [Katzenbach i Smith, 1994] znaleźć można kategory-
zację różnych typów zespołów, od grup roboczych do wydajnych zespołów specjalistów. Do-
kument IEEE-1058 cytowany w tym rozdziale poddawany jest obecnie rewizji i przeznaczony
do zatwierdzenia w 2009 roku. Rola architekta w wczesnych stadiach projektu realizowanego
zgodnie z metodologią Unified Process opisana jest w książce W. Royce'a [Royce, 1998].
Książka D. J. Paulisha [Paulish, 2001], traktująca o zarządzaniu projektami scentrali-
zowanymi na architekturze, zawiera szczegółowy opis korzyści, a zarazem wyzwań wynikają-
cych z sytuacji, gdy architektura oprogramowania definiowana jest równolegle z planem
zarządzania jego projektem.
Wszelkiego rodzaju szacowania ilościowe w inżynierii oprogramowania są zadaniem
trudnym i tylko nieliczne metody oferują w miarę dokładne wyniki w tym względzie. COCO-
MO II to model statystyczny szacowania wysiłku i czasu w oparciu o rozbudowany zestaw
parametrów, o czym przeczytać można w pracy B. Boehma, E. Horowitza, R. Madachy ego,
D. Reifera, B. K. Clarka, B. Steecea, A. W. Browna, S. Chulaniego i C. Abtsa [Boehm i in., 2000].
Na drugim końcu spektrum szacunkowego plasuje się Planning Poker. Opisana przez
J. Grenninga [Grenning, 2002] metoda szacowania sterowanego konsensusem: zgodnie z nią
wszyscy zainteresowani szacują pracochłonność poszczególnych zadań w formie modero-
wanej dyskusji. Zaletą tej metody jest wykorzystywanie doświadczenia uczestników przy jed-
noczesnym godzeniu sprzecznych celów. Metoda ta stała się popularna na gruncie „zwin-
nych" technik tworzenia oprogramowania, o czym pisze M. Cohn [Cohn, 2006].
Metoda Serum, której pomysłodawcami są Hirotaka Takeuchi i Ikujiro Nonaka, została
ostatecznie sformalizowana w latach 90. ubiegłego stulecia przez J. Sutherlanda i K. Schwabera
[Schwaber, 1995] i jest jedną z najwcześniejszych metod „zwinnego" zarządzania projektami.
678 Rozdział 12. • Zarządzanie racjonalizacją

Rysunek 14.13. Ogólny schemat metodologii Serum

Opisana jest ze szczegółami w książce K. Schwabera i M. Beedle'a [Schwaber i Beedle, 2002];


wiele innych książek, między innymi prace A. Elssamadisyego [Elssamadisy, 2009] i K. Schwa-
bera [Schwaber, 2004], wykorzystuje ją jako bazę dla pochodnych koncepcji, takich jak de-
finiowanie „zwinnych" wzorców projektowych.
Ze względów historycznych warto jeszcze wspomnieć o dwóch koncepcjach organizacji
projektów informatycznych, zaproponowanych przez F. P. Brooksa i G. M. Weinberga.
13.7. Ćwiczenia 679

Organizacja głównego programisty (chief programmer organization) [Brooks, 1995] czyni


odpowiedzialnym za projekt i implementację systemu głównego programistę, który deleguje
poszczególne zadania do wyspecj alizowanych programistów. Dla kontrastu, programowanie
bezosobowe (egoless programming) [Weinberg, 1971] opiera się na w pełni demokratycznej
strukturze samoorganizujących się zespołów Serum. Czytelników zainteresowanych dalszymi
szczegółami organizacji projektów odsyłamy do książki R. T. Wiganda, A. Picota i R. Rei-
chwalda [Wigand i in., 1997].

14.7. Ćwiczenia
14.1. Na rysunku 14.1 przedstawiliśmy diagram stanów reprezentujący poszczególne
fazy projektu. Wykorzystaj wzorzec projektowy Stan do przedstawienia każdej z tych
faz w postaci odrębnej klasy. Zastosuj opisywane w tym rozdziale aktywności za-
rządzania projektami do upublicznienia operacji tych klas. Zakładamy zespołową
organizację projektu.
14.2. Jakie są zalety obsady płaskiej w porównaniu z obsadą stopniowaną?
14.3. Jaka jest różnica między raportowaniem statusu a podejmowaniem decyzji na ze-
braniach?
14.4. Dlaczego role architekta oprogramowania i kierownika zespołu powinny być pełnione
przez różne osoby?
14.5. Narysuj model UML organizacji zespołowej projektu ARENA dla każdej z głównych
faz (koncepcyjnej, definicyjnej, startowej, ustalonej i końcowej).
14.6. Narysuj model zadań związanych z projektowaniem systemu MyTrip opisywanego
w rozdziale 6. „Projektowanie systemu — dekompozycja na podsystemy".
14.7. Oszacuj czas potrzebny do wykonania każdego z zadań modelu, o którym mowa
w ćwiczeniu 14.6. Zidentyfikuj ścieżkę krytyczną tego modelu.
14.8. Zidentyfikuj i określ priorytety pięciu najważniejszych czynników ryzyka związanych
z projektowaniem systemu MyTri p oraz zaplanuj sposoby ich minimalizowania.
14.9. Zidentyfikuj i określ priorytety pięciu najważniejszych czynników ryzyka zwią-
zanych z projektowaniem interfejsu użytkownika systemu CTC opisywanego w roz-
dziale 12. „Zarządzanie racjonalizacją". Zaplanuj sposoby minimalizowania tego
ryzyka.
14.10. System operacyjny Linux, stanowiący spektakularny owoc zastosowania modelu ba-
zarowego, jest bardziej niezawodny i bardziej reaktywny niż wiele innych systemów
operacyjnych przeznaczonych dla procesorów firmy Intel. Zaproponuj argumenty na
rzecz zastosowania modelu bazarowego do opracowania systemu sterującego
komputerami promu kosmicznego oraz przeciwko takiemu rozwiązaniu.
14.11. Uczestnicy projektu podzieleni zostali na zespoły czteroosobowe. Każdy zespół ma
do dyspozycji następujące zasoby: 2 jajka, 1 szpulkę taśmy samoprzylepnej, 1 rolkę
papieru toaletowego, filiżankę wody, wiadro z dwoma litrami piasku, 20 piłeczek
piankowych o średnicy ok. 1 cm każda i tablicę, której powierzchnia ma się znajdować
ok. 1 metra nad podłogą. Każdy zespół ma 25 minut na zbudowanie i przetestowanie
680 Rozdział 12. • Zarządzanie racjonalizacją

artefaktu zapewniającego, że jajko spuszczone n a w s p o m n i a n ą tablicę z wysokości


75 c m nie rozbije się. Czas prezentacji rozwiązania przez zespół nie m o ż e przekroczyć
5 minut.
14.12. Podobnie, w r a m a c h struktury czteroosobowych zespołów, każdy zespół m a d o dys-
pozycji następujące zasoby: 2 pudełka klocków D U P L O i 2 tablice o d d a l o n e o d siebie
o 1,5 metra. Każdy zespół m a 25 m i n u t n a z b u d o w a n i e i przetestowanie m o s t u mię-
dzy w s p o m n i a n y m i tablicami, wyłącznie ze w s p o m n i a n y c h klocków, który to m o s t
nie zawali się p r z e d u p ł y w e m 1 m i n u t y o d zakończenia b u d o w y . Zespół m a 5 m i n u t
czasu n a z a p r e z e n t o w a n i e r o z w i ą z a n i a .

14.13. Z a s t o s u j p o s z c z e g ó l n e m o d e l e z a r z ą d z a n i a dla p r o j e k t u b ę d ą c e g o p r z e d m i o t e m
ćwiczenia 14.11.
14.14. S k o n s t r u u j S P M P dla p r o j e k t u b ę d ą c e g o p r z e d m i o t e m ćwiczenia 14.11.

14.15. C o reprezentuje p o z i o m y odcinek krzywej wygaszania przebiegu, n a przykład odcinek


m i ę d z y d n i a m i t r z e c i m a s z ó s t y m n a w y k r e s i e z r y s u n k u 14.12?

Bibliografia
[Albrecht i Gafifhey, 1983] A. J. Albrecht, J. E. Gaffney, jr. Software function, source lines of code, and
development effort prediction: A software science validation, „IEEE Transactions
on Software Engineering", t. SE-9, nr 6, listopad 1983.
[Boehm, 1991] B. Boehm Software risk management: Principles and practices, „IEEE Software",
t.l, str. 3 2 - 4 1 , 1991.
[Boehm i in., 2000] B. Boehm, E. Horowitz, R. Madachy, D. Reifer, B. K. Clark, B. Steece, A.
W. Brown, S. Chulani, C. Abts Software Cost Estimation with COCOMO
II, Prentice Hall, Upper Saddle River, NJ, 2000.
[Brooks, 1995] F. P. Brooks The Mythical Man Month, Anniversary Edition: Essays
on Software Engineering, Addison-Wesley, Reading, MA, 1995.
[Carr i in., 1993] M. J. Carr, S. L. Konda, I. Monarch, F. C. Ulrich, C. F. Walker Taxonomy-
Based Risk Identification, Technical Report CMU/SEI-93-TR-6, Software
Engineering Institute, Carnegie Mellon University, Pittsburgh, PA, 1993.
[Charette, 1989] R. N. Charette Software Engineering Risk Analysis and Management,
McGraw-Hill, New York, 1989.
[Cohn, 2006] M. Cohn Agile Estimating and Planning, Pearson, Upper Saddle River, NJ, 2006.
[Elssamadisy, 2009] A. Elssamadisy Agile Adoption Patterns, Addison-Wesley, Reading, MA, 2009.
[Grenning, 2002] J. Grenning, Planning Poker, http://wwwplanningpoker.com/2002.
[Halstead, 1977] M. H. Halstead Elements of Software Science, Elsevier, New York, 1977.
[IEEEStd. 1058-1998] IEEE Standard for Software Project Management Plans, IEEE Computer
Society, New York, 1998.
[Katzenbach i Smith, J. R. Katzenbach, D. K. Smith The Wisdom of Teams: Creating
1994] The High-Performance Organization, Harper Business, 1994.
[Kayser, 1990] T. A. Kayser Mining Group Gold, Serif, El Segundo, CA, 1990.
[McCabe, 1976] T. McCabe A software complexity measure, „IEEE Transactions on Software
Engineering", t. 2, nr 12, grudzień 1976.
Bibliografia 681

[Paulish, 2001] D. J. Paulish Architecture-Centric Software Project Management: A Practical


Guide, SEI Series in Software Engineering, Addison-Wesley, Reading, MA, 2001.
[Portny, 2001] S. E. Portny Project Management for Dummies, John Wiley & Sons, 2000.
[Rogers i in., 1986] The Presidential Commission on the Space Shuttle Challenger Accident
Report, Washington, DC, 6 czerwca 1986.
[Royce, 1998] W. Royce Software Project Management: A Unified Framework, Addison-
Wesley, Reading, MA, 1998.
[Schwaber, 1995] K. Schwaber „Scrum Development Process", Business Object Design and
Implementation Workshop, OOPSLA'95, Austin, TX, październik 1995.
[Schwaber i Beedle, 2002] K. Schwaber, M. Beedle Agile Software Development with Scrum, Prentice
Hall, Upper Saddle River, 2002.
[Schwaber, 2004] K. Schwaber Agile Project Management With Scrum, Microsoft, Redwood, 2004.
[Vaughan, 1996] D. Vaughan The Challenger Launch Decision: Risky Technology, Culture,
and Deviance at NASA, The University of Chicago Press, Chicago, 1996.
[Weinberg, 1971] G. M. Weinberg The Psychology of Computer Programming, Van Nostrand,
New York, 1971.
[Wigandiin., 1997] R. T. W i g a n d , A. Picot, R. Reichwald Information, Organization and
Management: Expanding Markets and Corporate Boundaries, John Wiley
& Sons, London, 1997.
15.1. Wstęp: nawigacja polinezyjska 684

15.2. IEEE 1074: Standard cykli życiowych 688


15.2.1. Procesy i aktywności 688
15.2.2. Modelowanie cyklu życiowego 690
15.2.3. Zarządzanie projektem 690
15.2.4. Prerealizacja 691
15.2.5. Realizacja — tworzenie systemu 692
15.2.6. Postrealizacja 693
15.2.7. Procesy integralne (międzyrealizacyjne) 694

15.3. Charakteryzowanie dojrzałości modeli cyklu życiowego 695

15.4. Modele cyklu życiowego 698


15.4.1. Sekwencyjne modele ukierunkowane na aktywności 699
15.4.2. Iteracyjne modele ukierunkowane na aktywności 701
15.4.3. Modele ukierunkowane na encje 706

15.5. Literatura uzupełniająca 709

15.6. Ćwiczenia 710

Bibliografia 710
Cykl życiowy
oprogramowania

Rzeczywistość musi odbiegać od koncepcji,


bo w przeciwieństwie do nich jest żywa i dynamiczna.
— Robert Pirsig Lila

O y y k l życiowy oprogramowania to całokształt aktywności i produktów związanych z proce-


sami niezbędnymi do wytworzenia systemu informatycznego. Modele cyklu życiowego umoż-
liwiają menedżerom i programistom zmaganie się ze złożonością tego procesu na takiej samej
zasadzie, jak modele analityczny i modele projektu systemu okazują się pomocne w radzeniu
sobie z jego złożonością. W przypadku modeli systemów modelowaniu podlega otaczająca nas
rzeczywistość ze swymi przedmiotami i zjawiskami — zegarkami, wypadkami, pociągami,
czujnikami czy budynkami; rzeczywistość procesu tworzenia oprogramowania obraca się
wokół innych encji: uczestników, zespołów, aktywności i produktów. W literaturze poświęco-
nej tematyce cyklu życiowego oprogramowania opisano wiele modeli, zmierzających do lep-
szego rozumienia i kontrolowania procesu tworzenia tego oprogramowania, a także ujmowania
rozmaitych aspektów tego procesu w kategoriach ilościowych. Modele te uwidaczniają aktyw-
ności wspomnianego procesu i zależności między nimi, co sprawia, że stają się łatwiejsze
w zarządzaniu.
W tym rozdziale powrócimy do aktywności opisywanych wcześniej, ale przedstawimy
je z perspektywy modelowania cyklu życiowego oprogramowania. W modelowaniu tym mogą
być przydatne techniki opisywane we wcześniejszych rozdziałach; chociaż modele cyklu ży-
ciowego wyrażane są najczęściej w postaci definiowanej ad hoc wygodnej notacji, postaramy
się — zgodnie z konwencją tej książki — używać w tym celu diagramów UML wszędzie tam,
gdzie jest to możliwe. Opiszemy więc najpierw typowe aktywności i produkty związane z za-
rządzaniem cyklem życiowym, w oparciu o definicję standardu IEEE 1074 [IEEE Std. 1074-2006],
po czym przedstawimy model dojrzałości organizacyjnej — framework umożliwiający ocenę
dojrzałości organizacji projektu i jej cyklu życiowego. Następnie przedyskutujemy różne model
cyklu życiowego, zaproponowane w związku z tworzeniem złożonych systemów: model ka-
skadowy, model spiralny, V-model i jednolity proces wytwarzania oprogramowania. Zajmiemy
się także modelem cyklu życiowego, nazywanym modelem zagadnieniowym, zgodnie z którym
produkty i aktywności modelowane są jako zbiór zagadnień magazynowanych w bazie wiedzy.
Jest to model skoncentrowany na encjach cyklu życiowego, łatwo przystosowujący się do czę-
stych zmian w projekcie.
684 Rozdział 15. • Cykl życiowy oprogramowania

15.1. Wstęp nawigacja polinezyjska


Nawigacja polinezyjska
Polinezja to duży archipelag tworzący w przybliżeniu trójkąt, z wyspą Tahiti pośrodku, Hawajami
na północy, Wyspą Wielkanocną na wschodzie i Nową Zelandią u południowego zachodu. Poline-
zyjczycy przywędrowali tutaj z południowo-wschodniej Azji, zasiedlając wyspy Pacyfiku systema-
tycznie od zachodu na wschód. Osiedlili się na wiele stuleci wcześniej, niż Kolumb zdążył dotrzeć na
Karaiby: na Tahiti czternaście wieków przed Chrystusem, na Hawajach w piątym wieku naszej ery,
zaś na Nowej Zelandii w wiekach od dziesiątego do piętnastego naszej ery. Rybacy polinezyjscy od-
krywali kolejne wyspy okazjonalnie, a proces zasiedlania nowej wyspy był na wskroś wyszukany,
wiązał się bowiem nie tylko z migracją całych rodzin, lecz także z przeniesieniem plantacji bananów,
trzciny cukrowej i ignamów oraz udomowionych zwierząt — psów, świń i kurczaków.

To wszystko zdumiewa tym bardziej, iż pod względem cywilizacyjnym Polinezyjczycy plasowali


się wówczas w epoce kamiennej. Budowali wielokadłubowe kanu za pomocą narzędzi wykona-
nych z kości i kamienia, bez używania jakiegokolwiek metalu. Ich kanu zaprojektowane zostały
z myślą o pozostawaniu na stałym kursie i odporności na sztormy, a zdolne były poruszać się
znacznie szybciej niż ówczesne statki europejskie. Wiedzę na temat morskich podróży i lokali-
zacji odkrywanych wysp przekazywali w formie ustnej tradycji, z pokolenia na pokolenie.

Polinezyjczycy nie znali instrumentów nawigacyjnych, odnajdywali drogę i utrzymywali kurs na


podstawie miejsc wschodu i zachodu gwiazd, kierunku wiatru i fal oceanu. Umiejętności te kulty-
wowane są do dziś przez mieszkańców mikronezyjskiej wyspy Truk, na wschodzie Polinezji; dzięki
temu łatwiej można zrozumieć, jak w ogóle możliwe było pokonywanie wielu tysięcy kilometrów
przy użyciu kanu, bez pomocy kompasu. W pobliżu równika gwiazdy poruszają się na nieboskłonie
wokół osi północ-południe. Altair na przykład porusza się ze wschodu na zachód. Niektóre gwiazdy,
jak Vega, przesunięte są bardziej ku północy, inne — jak Antares — bardziej na południe. Nawigator,
stając twarzą w kierunku wschodzącej Vegi, powinien obrócić się plecami do miejsca zachodu Antaresa;
generalnie, zapamiętując relatywne położenie szesnastu gwiazd, zawsze był w stanie odpowiednio
skorygować kurs. Za dnia oraz w czasie pochmurnych nocy, gdy gwiazdy były niewidoczne, nawi-
gator wykorzystywał kierunek wiatru i fal oceanicznych, które zapamiętał w odniesieniu do położenia
znajomych gwiazd.

W pobliżu lądu zmienia się kształt i odgłos fal morskich. Można wówczas wypatrywać specyficznych
gatunków ptactwa lądowego, które rankiem powinno kierować się w głąb morza (na rybki), by wie-
czorem powracać na wyspę. Wystarczy więc wieczorami podążać za lotem ptaków, zaś rano kierować
się w stronę punktu, z którego zaczynają swoją wyprawę. Dla odmiany, budowniczowie statków ery
Kolumba od tysięcy lat z powodzeniem wykorzystywali konstrukcje metalowe. Z lektury dzienników
Kolumba można wywnioskować, że nie posługiwał się nawigacją gwiezdną, praktykując w zamian
nawigację zliczeniową 1 : w utrzymywaniu kursu pomagał sternikowi kompas magnetyczny, ponadto
co godzinę dokonywany był pomiar prędkości statku, polegający na wyrzuceniu za burtę jakiegoś

1
Ang. dead reckoning, dosł. „tępe zliczanie", to przybliżone ustalanie bieżącej pozycji na podstawie
znajomości pozycji poprzedniej, szacowanej prędkości oraz określonego kierunku. Wraz z upo-
wszechnieniem łączności radiowej usunięte zostały w cień metody nawigacji zliczeniowej opartej na
kompasie, ale nieoczekiwanie narodziły się w nowym wcieleniu — w postaci rozmaitych systemów żyro-
skopowych. Ze względu na przyrostowy charakter procesu, jakim jest nawigowanie zliczeniowe, występuje
tendencja do kumulowania błędów w ustalaniu kolejnych pozycji. W nawigacji zliczeniowej na morzu
dodatkowym źródłem błędu jest zaniedbywanie wpływu wiatru i prądów morskich na prędkość i kie-
runek poruszania się statku — przyp. tłum.
w efekcie nieznacznie, acz systematycznie ma południe; ponieważ jednocześnie prądy morskie znosiły
go na północ, nie zdawał sobie sprawy z ograniczeń kompasu magnetycznego.

b o w i e m d r o g a d o Indii w i o d ł a w o k ó ł w y b r z e ż y A f r y k i i m e t o d y nawigacji zliczeniowej d a w a ł y

w p o d r ó ż y transatlantyckiej. W o b u p r z y p a d k a c h — Polinezyj czyków i Krzysztofa K o l u m b a —


n a w i g a t o r m i a ł swój cel, o d p o w i e d n i o : zasiedlenie w y s p y i znalezienie d r o g i d o I n d i i p r z e z
A t l a n t y k , w o b u p r z y p a d k a c h d ą ż e n i e d o tego celu r e a l i z o w a n e b y ł o w d r o d z e z a s t o s o w a n i a

l ą d u i w y l n a c z a n i i o r a z k o r y g o w a n i ! k u r s u ) . G d y b y t a k doszło d o s p o t k a n i a załogi K o l u m b a

kłemodde nawigacji.^ ^ ^

k t ó r y m n a s t ę p u j e jego e k s p l o a t o w a n i e . A k t y w n o ś c i p r o c e s u n i e m u s z ą u k ł a d a ć się w s t r u k -
t u r ę ściśle s e k w e n c y j n ą , czego p r z y k ł a d w i d z i m y n a r y s u n k u 15.3: t w o r z e n i e s y s t e m u i b u d o -

Cykl życiowy z ^ s u n k u 15.2, t y ^ L L ^ widzimy na rysunku


686 Rozdział 15. • Cykl życiowy oprogramowania

Rysunek 15.1. Przykład prostego cyklu życiowego oprogramowania

Rysunek 15.2. Zależności czasowe między aktywnościami cyklu życiowego

Rysunek 15.3. Inny przykład zależności czasowych między aktywnościami cyklu życiowego

Rysunek 15.4. W i d o k cyklu życiowego w ujęciu produktowym


15.1. Wstęp: nawigacja polinezyjska 687

O b a ujęcia cyklu życiowego — w k a t e g o r i a c h a k t y w n o ś c i i w k a t e g o r i a c h p r o d u k t ó w —


są w z g l ę d e m siebie k o m p l e m e n t a r n e , co z o b a c z y ć m o ż n a n a r y s u n k u 15.5. K a ż d y p r o d u k t
g e n e r o w a n y jest p r z e z j e d n ą l u b więcej aktywności: a k t y w n o ś ć d e f i n i o w a n i a p r o b l e m u w y k o -
rzystuje d o k u m e n t analizy r y n k u , a jej r e z u l t a t e m jest d o k u m e n t analizy w y m a g a ń ; z a w a r t o ś ć
tego d o k u m e n t u w y k o r z y s t y w a n a jest przez a k t y w n o ś ć t w o r z e n i a systemu, w w y n i k u k t ó r e j
o t r z y m u j e m y w y k o n y w a l n y system. T e n system jest p r o d u k t e m w e j ś c i o w y m d o swej w ł a s n e j
eksploatacji, k t ó r e j w y n i k i e m jest d o k u m e n t w n i o s k ó w . Analogicznie, o w o c e m k a ż d e j aktyw-
n o ś c i jest j e d e n l u b w i ę c e j p r o d u k t ó w .

Rysunek 15.5. Powiązanie aktywności i produktów prostego cyklu życiowego

W sekcji 15.2 o p i s z e m y a k t y w n o ś c i cyklu ż y c i o w e g o o p r o g r a m o w a n i a z d e f i n i o w a n e


w s t a n d a r d z i e IEEE 1074 [IEEE Std. 1074-2006], w p r o w a d z a j ą c y m p r e c y z y j n e definicje u m o ż -
liwiające u c z e s t n i k o m p r o j e k t u efektywne i j e d n o z n a c z n e k o m u n i k o w a n i e się. O p i s z e m y także
przepływ informacji między aktywnościami.
W sekcji 15.3 o p i s z e m y m o d e l dojrzałości o r g a n i z a c y j n e j — f r a m e w o r k p r z e z n a c z o n y
d o o c e n y dojrzałości organizacji i ich cykli życiowych o r a z p o r ó w n y w a n i a p r o j e k t ó w i o r g a n i -
zacji n a p o d s t a w i e p r z e j a w i a n y c h a k t y w n o ś c i c y k l u ż y c i o w e g o .
W sekcji 15.4 d o k o n a m y p r z e g l ą d u r o z m a i t y c h m o d e l i cyklu życiowego z o r i e n t o w a n y c h
n a a k t y w n o ś c i i r ó ż n i ą c y c h się u p o r z ą d k o w a n i e m t y c h a k t y w n o ś c i . P r z e d y s k u t u j e m y d w a
m o d e l e sekwencyjne: k a s k a d o w y , o p i s a n y p r z e z W . R o y c e a [Royce, 1970], i V - m o d e ł , o m ó -
w i o n y p r z e z R. W . Jensena i C. C. T o n i e s a [Jensen i Tonies, 1979], oraz d w a m o d e l e iteratywne:
m o d e l s p i r a l n y , k t ó r e g o o p i s z n a l e ź ć m o ż n a w p r a c y B. B o e h m a [ B o e h m , 1987], i j e d n o l i t y
proces wytwarzania o p r o g r a m o w a n i a , k t ó r y został o m ó w i o n y w książce J. Jacobsona, G. B o o c h a
i J. R u m b a u g h a [Jacobson i in., 1999]. Z a j m i e m y się t a k ż e m o d e l e m z o r i e n t o w a n y m n a e n c j e
cyklu życiowego, b a z u j ą c y m n a zagadnieniach.
688 Rozdział 15. • Cykl życiowy oprogramowania

15.2. IEEE 1074: standard cykli życiowych


S t a n d a r d IEEE Standardfor Software Life Cycle Processes d e f i n i u j e zbiór aktywności i p r o c e s ó w
o b o w i ą z k o w y c h dla t w o r z e n i a i p i e l ę g n o w a n i a o p r o g r a m o w a n i a [IEEE Std. 1074-2006]. Sfor-
m u ł o w a n y z o s t a ł z m y ś l ą o s t w o r z e n i u w s p ó l n e g o f r a m e w o r k u dla o p r a c o w y w a n i a m o d e l i
cyklu życiowego i d o s t a r c z e n i u p r z y k ł a d ó w t y p o w y c h sytuacji w tej dziedzinie. P r z e d s t a w i m y
główne aktywności definiowane przez powyższy standard i wyjaśnimy jego f u n d a m e n t a l n e
koncepcje, używając diagramu U M L .

15.2.1. Procesy i aktywności


P r o c e s jest z b i o r e m a k t y w n o ś c i w y k o n y w a n y c h z m y ś l ą o osiągnięciu p e w n e g o celu (zarzą-
d z a n i a w y m a g a n i a m i , d o s t a r c z a n i a p r o d u k t ó w i t y m p o d o b n y c h ) . S t a n d a r d IEEE w y m i e n i a
s i e d e m n a ś c i e p r o c e s ó w , p o g r u p o w a n y c h w j e d n o s t k i wyższego r z ę d u , z w a n e g r u p a m i p r o -
c e s ó w ( p a t r z tabela 15.1). P r z y k ł a d a m i t a k i c h g r u p są: z a r z ą d z a n i e p r o j e k t e m , prerealizacja,
t w o r z e n i e s y s t e m u i postrealizacja. P r z y k ł a d a m i p r o c e s ó w w c h o d z ą c y c h w skład g r u p y „ t w o -
r z e n i e s y s t e m u " są:

• proces przetwarzania wymagań, w ramach którego programiści opracowują modele


systemu,
• proces projektowania, polegający na dekompozycji systemu na podsystemy,
• proces implem en tow a n ia, o w o c u j ą c y f i z y c z n y m i p o s t a c i a m i p o s z c z e g ó l n y c h k o m -
ponentów.

Tabela 15.1. Procesy inżynierii oprogramowania definiowane przez standard IEEE 1074

Grupa procesów Procesy


Modelowanie cyklu życiowego Wybór modelu cyklu życiowego

Zarządzanie projektem Inicjowanie projektu


Monitorowanie i kontrolowanie projektu
Zarządzanie jakością oprogramowania

Prerealizacja Eksplorowanie koncepcji


Alokacja systemu

Tworzenie systemu Przetwarzanie wymagań


Projektowanie
Implementowanie

Postrealizacja Instalowanie
Eksploatacja i wsparcie dla użytkowników
Pielęgnowanie
Wycofanie z eksploatacji

Procesy integralne Weryfikowanie i walidacja


Zarządzanie konfiguracją oprogramowania
Opracowywanie dokumentacji
Szkolenie personelu
15.2. IEEE 1074: standard cykli życiowych 689

Każdy proces złożony jest z aktywności. Aktywność to zadanie (lub grupa podaktywności)
przypisane uczestnikowi projektu lub zespołowi i realizowane, by osiągnąć pewien cel. Przykła-
dowo proces przetwarzania wymagań składa się z trzech aktywności. Oto one.

• Zdefiniowanie i opracowanie wymagań funkcyjnych, precyzyjnie opisujących elementy


funkcjonalne systemu.
• Zdefiniowanie wymagań pod adresem interfejsu użytkownika, precyzujących detale
interakcji użytkownika z systemem.
• Zintegrowanie wymagań i zróżnicowanie ich priorytetów, w celu zapewnienia ich
spójności i uwzględnienia priorytetów użytkownika.

Wykonywanie zadań wiąże się z konsumowaniem zasobów (personelu, czasu, pieniędzy)


i wytwarzaniem produktów. Na etapie planowania aktywności dekomponowane są na zadania
specyficzne dla projektu, każde z tych zadań zostaje przypisane uczestnikowi lub zespołowi
i dla każdego wyznaczone zostają daty graniczne (patrz rysunek 15.6). W trakcie realizacji
projektu wykonywana praca jest kontrolowana pod kątem zgodności z planami, a także w celu
ewentualnego przemieszczania zasobów w związku z napotykanymi problemami.

Rysunek 15.6. Model cyklu życiowego oprogramowania. Na cykl ten składają się grupy procesów,
każdy proces jest dążeniem do określonego celu (sformułowania wymagań, zaprojektowania syste-
mu, jego zainstalowania i tym podobnych). Proces składa się z aktywności, które z kolei podzielić
można na zadania lub podaktywności. Zadanie r e p r e z e n t u j e najmniejszą, niepodzielną część pracy
z perspektywy zarządzania nią. Każde zadanie zużywa zasoby i skutkuje wytworzeniem jednego lub
więcej produktów. Projekt jest instancją cyklu życiowego, traktowanego jako klasy
690 Rozdział 15. • Cykl życiowy oprogramowania

Spośród widocznych w tabeli 15.1 procesów definiowanych przez standard IEEE 1074,
z punktu widzenia programisty, sześć pierwszych — tych związanych z modelowaniem cyklu
życiowego, zarządzaniem projektem i prerealizacją — często już zainicjowano, zanim dołączy
on do projektu.

15.2.2. Modelowanie cyklu życiowego


W ramach modelowania cyklu życiowego menedżer projektu przystosowuje aktywności zde-
finiowanie w IEEE 1074 do specyfiki projektu (który jest instancją cyklu życiowego jako klasy).
Różne procesy cechują się bowiem różnymi aktywnościami i odmiennym ich uporządkowa-
niem, przykładowo w projekcie, w którym nie występuje potrzeba strukturalnego przecho-
wywania trwałych danych, brakuje aktywności Projektowanie bazy danych. Wybrany model
cyklu życiowego staje się materiałem wejściowym dla procesu inicjowania projektu, opisywa-
nego w następnej sekcji.

15.2.3. Zarządzanie projektem


W ramach zarządzania projektem menedżer inicjuje, monitoruje i kontroluje projekt przez cały
cykl życiowy. Na zarządzanie projektem składają się trzy procesy (patrz tabela 15.2). Inicjowanie
projektu to budowanie jego infrastruktury, czyli definiowanie planu zadań, harmonogramu,
budżetu, organizacji i środowiska — to ostatnie obejmuje standardy projektowania, infrastruk-
turę komunikacyjną, procedury prowadzenia zebrań i raportowania problemów oraz metodo-
logie i narzędzia programistyczne. Większość informacji generowanych w tym procesie zostaje
udokumentowana w planie zarządzania projektem (SPMP). Proces inicjowania projektu zo-
staje zakończony wraz ze zbudowaniem stabilnego środowiska projektowego.

Tabela 15.2. Procesy składające się na zarządzanie projektem

Proces Paragraf2 Aktywności


Inicjowanie projektu 3.1.3 Odwzorowywanie aktywności w model cyklu życiowego
3.1.4 Przydzielanie zasobów do projektu
3.1.5 Budowanie środowiska projektowego
3.1.6 Planowanie zarządzania projektem

Monitorowanie 3.2.3 Analizowanie ryzyka


i kontrolowanie 3.2.4 Sporządzanie planów awaryjnych
projektu
3.2.5 Realizowanie bieżących zadań zarządzania projektem
3.2.6 Rejestrowanie zdarzeń
3.2.7 Implementowanie modelu raportowania problemów

Zarządzanie jakością 3.3.3 Planowanie zarządzania jakością oprogramowania


oprogramowania 3.3.4 Definiowanie metryk
3.3.5 Nadzorowanie jakości oprogramowania
3.3.6 Identyfikowanie obszarów wymagających poprawy jakości

2
Ta kolumna tabeli (i następnych tabel w tym rozdziale) zawiera n u m e r odnośnego paragrafu
w dokumencie standardu IEEE 1074.
15.2. IEEE 1074: standard cykli życiowych 691

Monitorowanie i kontrolowanie projektu to proces zapewniający, że projekt realizowany


jest zgodnie z planem zadań i budżetem. Gdy menedżer projektu zaobserwuje jakiekolwiek
odstępstwo od harmonogramu, powinien podjąć działania korekcyjne w postaci realokacji za-
sobów, zmiany procedur lub reorganizacji harmonogramu. Wszelkie zmiany tego typu po-
winny być odzwierciedlone w dokumencie SPMP. Proces monitorowania i kontrolowania
projektu aktywny jest przez cały jego cykl życiowy.
Zarządzanie jakością oprogramowania zapewnia, że konstruowany system spełnia wy-
magane standardy jakościowe (określone w czasie inicjowania projektu). Proces ten realizo-
wany jest przez osobny zespół kontroli jakości, a to w związku z oczywistą rozbieżnością in-
teresów: programiści pragną ukończyć system w terminie, zaś celem kontrolerów jakości jest
zapobieżenie uznania systemu za ukończony, zanim nie spełni on wymaganych standardów
jakościowych. Proces zarządzania jakością projektu jest aktywny przez większą część jego
cyklu życiowego.
W rozdziale 14. „Zarządzanie projektem" opisywaliśmy te aspekty inicjowania pro-
jektu oraz jego monitorowania i kontrolowania, które były związane z planowaniem, or-
ganizacją i śledzeniem realizacji. Aktywność budowania środowiska projektowego wymaga
szczególnej uwagi w kontekście projektu realizowanego w podziale na zespoły: jedną z krytycz-
nych części wspomnianego środowiska jest infrastruktura komunikacyjna, zapewniająca
rozpowszechnianie istotnych informacji wśród uczestników. Aby łatwo można było re-
agować na zmiany i raportować problemy, wszyscy uczestnicy projektu powinni być świa-
domi sposobów przepływu i rozpowszechniania informacji w ramach projektu. Aktywności
związane z budowaniem infrastruktury komunikacyjnej projektu opisywaliśmy w rozdziale 3.
„Organizacja projektu i komunikacja"; jak pamiętamy, definiowanie tej infrastruktury odbywa
się na bazie podziału uczestników na zespoły, a podział z kolei uwarunkowany jest początkową
definicją architektury systemu (tworzoną w ramach procesu alokacji systemu).

15.2.4. Prerealizacja
W ramach procesu prerealizacji menedżer (lub marketingowcy) i klient identyfikują ideę pro-
jektu lub przesłanki zapotrzebowania na jego realizację. Realizacja ta może mieć postać inży-
nierii pierwotnej, czyli tworzenia systemu od zera (patrz sekcja 4.3.5), albo inżynierii wtórnej,
czyli unowocześniania (zastępowania) istniejącego systemu lub też jedynie zmiany jego inter-
fejsu. Proces alokacji systemu prowadzi do ustalenia jego początkowej architektury oraz iden-
tyfikacji wymagań związanych ze sprzętem, oprogramowaniem i operacjami. Zauważmy, że
dekompozycja systemu stanowi podbudowę infrastruktury komunikowania się uczestników
projektu. Wymagania, dekompozycja systemu i infrastruktura systemu opisane są w deklara-
cji problemu 3 , która stanowi materiał wejściowy dla procesu tworzenia systemu. Proces pre-
realizacji przedstawiony jest w tabeli 15.3.

3
Deklaracja potrzeby (Statement of Need) wymieniona w standardzie IEEE 1074 podobna jest do
deklaracji problemu, nie zawiera jednak żadnych odniesień do organizacji projektu.
692 Rozdział 15. • Cykl życiowy oprogramowania

Tabela 15.3. Proces prerealizacji

Proces Paragraf Aktywności


Eksploracja 4.1.3 Identyfikowanie pomysłu lub potrzeby
koncepcji 4.1.4 Formułowanie potencjalnie możliwych podejść do realizacji projektu
4.1.5 Przeprowadzanie analizy opłacalności
4.1.6 Planowanie migracji danych systemu (jeśli takowa jest przewidziana)
4.1.7 Ulepszanie i finalizowanie pomysłu lub potrzeby

Alokacja systemu 4.2.3 Analizowanie funkcji systemu


4.2.4 Opracowywanie architektury systemu
4.2.5 Dekompozycja wymagań stawianych systemowi

15.2.5. Realizacja — tworzenie systemu


Tworzenie systemu obejmuje procesy związane z jego konstruowaniem (patrz tabela 15.4).

Tabela 15.4. Procesy wchodzące w skład tworzenia systemu

Proces Paragraf Aktywności


Przetwarzanie 5.1.3 Definiowanie i opracowywanie wymagań stawianych systemowi
wymagań 5.1.4 Definiowanie wymagań dotyczących interfejsu systemu
5.1.5 Integrowanie wymagań i różnicowanie ich priorytetów

Projektowanie 5.2.3 Projektowanie architektury systemu


5.2.4 Projektowanie bazy danych (jeśli jest przewidziana w projekcie)
5.2.5 Projektowanie interfejsów
5.2.6 Wybieranie i opracowywanie algorytmów (jeśli ma zastosowanie)
5.2.7 Projektowanie szczegółowe

Implementacja 5.3.3 Tworzenie danych testowych


5.3.4 Tworzenie kodu źródłowego
5.3.5 Generowanie binariów
5.3.6 Tworzenie dokumentacji operacyjnej
5.3.7 Planowanie integracji
5.3.8 Przeprowadzanie integracji

Przetwarzanie wymagań rozpoczyna się od ich nieformalnego opisania, a następnie wy-


rażenia w kategoriach wysokopoziomowych elementów funkcjonalnych systemu, stworzenia
kompletnej specyfikacji systemu i zróżnicowania priorytetów wymagań. Przetwarzanie wy-
magań opisywaliśmy w rozdziałach 4. „Zbieranie wymagań" i 5. „Analiza wymagań". Proces
projektowania to przetwarzanie architektury zdefiniowanej w procesie alokacji systemu oraz
specyfikacji wymagań w spójną i dobrze określoną reprezentację systemu. Projektowanie ar-
chitektury systemu i projektowanie jego interfejsów to aktywności polegające na doskonaleniu
dekompozycji systemu. Obejmują one także mapowanie wymagań w elementy sprzętu i opro-
gramowania, opisywanie warunków granicznych, wybór gotowych komponentów „z półki"
i definiowanie celów projektowych. Projektowanie szczegółowe to dookreślanie detali poszczę-
15.2. IEEE 1074: standard cykli życiowych 693

gólnych podsystemów. Efektem procesu projektowania jest projekt obiektów, ich atrybutów
i operacji oraz zorganizowanie ich w pakiety; po zakończeniu tego procesu zdefiniowane są
wszystkie metody i ich sygnatury, a także dodane nowe klasy wynikające z wymagań poza-
funkcyjnych i szczegółów poszczególnych komponentów. W tej książce proces projekto-
wania przewija się przez treść rozdziałów 6. „Projektowanie systemu — dekompozycja na
podsystemy", 8. „Projektowanie obiektów: wielokrotne wykorzystywanie rozwiązań wzor-
cowych" i 9. „Projektowanie obiektów: specyfikowanie interfejsów".
Proces implementacji to przetwarzanie modelu projektu systemu na jego wykonywalny
odpowiednik. Elementami tego procesu są aktywności planowania i przeprowadzania inte-
gracji systemu. Zwróćmy uwagę, że testy przeprowadzane w ramach tego procesu są niezależne
od tych, które wchodzą w skład weryfikowania i walidacji. W rozdziale 11. „Testowanie" opi-
saliśmy testowe i integracyjne aspekty implementowania systemu.

15.2.6. Postrealizacja
Proces postrealizacji obejmuje aktywności instalowania, pielęgnowania, eksploatowania, wsparcia
technicznego systemu i (ewentualnego) wycofywania z eksploatacji jego poprzednika (patrz
tabela 15.5).

Tabela 15.5. Procesy postrealizacji

Proces Paragraf Aktywności


Instalacja 6.1.3 Planowanie instalacji
6.1.4 Dystrybuowanie oprogramowania
6.1.5 Instalowanie oprogramowania
6.1.7 Akceptowanie oprogramowania w jego środowisku operacyjnym

Eksploatacja 6.2.3 Eksploatowanie systemu


i wsparcie techniczne 6.2.4 Udzielanie pomocy technicznej i konsultacji
6.2.5 Utrzymywanie dziennika żądań pomocy

Pielęgnacja 6.3.3 Ponowne stosowanie cyklu życiowego

Wycofanie poprzedniej 6.4.3 Powiadamianie użytkowników


wersji z eksploatacji 6.4.4 Prowadzenie równoległego przetwarzania (jeśli ma zastosowanie)
6.4.5 Zaprzestanie eksploatowania starszej wersji systemu

Instalowanie systemu polega na umieszczeniu jego binariów i danych towarzyszących


w środowisku wskazanym przez klienta. Ostatnim aktem instalacji jest zaakceptowanie sys-
temu przez klienta na podstawie przeprowadzonych testów akceptacyjnych, weryfikujących
spełnienie kryteriów zapisanych w umowie projektu. Proces eksploatacji i wsparcia technicz-
nego koncentruje się wokół eksploatowania systemu i szkolenia jego użytkowników. Pielę-
gnacja systemu to usuwanie usterek i błędów w oprogramowaniu, ujawnionych po jego do-
starczeniu; w kategoriach cyklu życiowego projektu oznacza przeniesienie wszystkich procesów
i aktywności tego cyklu do nowego projektu. Wycofanie systemu z eksploatacji następuje
694 Rozdział 15. • Cykl życiowy oprogramowania

w wyniku zastąpienia go nową wersją; wiąże się także z zakończeniem obsługi dotychczasowej
wersji. Aby przejście na nową wersję systemu odbyło się bez większych perturbacji, przez pewien
czas eksploatowane są równolegle obie wersje, zanim użytkownicy nie „przesiądą" się w pełni
na nową. W tej książce opis procesów grupy postrealizacji ograniczamy do dostarczenia systemu
klientowi i wykonania przez niego testów akceptacyjnych.

15.2.7. Procesy integralne (międzyrealizacyjne)


Niektóre procesy nie są umiejscowione w ścisłych punktach cyklu życiowego oprogramowania,
lecz przewijają się przez cały ten cykl. Takie procesy nazywane są procesami integralnymi
lub procesami międzyrealizacyjnymi. Należą do nich: walidacja i weryfikacja, zarządzanie
konfiguracją oprogramowania, opracowywanie dokumentacji i szkolenie użytkowników (patrz
tabela 15.6).

Tabela 15.6. Procesy integralne

Proces Paragraf Aktywności


Weryfikacja 7.1.3 Planowanie weryfikacji i walidacji
i walidacja 7.1.4 Wykonywanie zadań związanych z weryfikacją i walidacją
7.1.5 Kolekcjonowanie i analizowanie wartości metryk
7.1.6 Planowanie testowania
7.1.7 Opracowywanie testów weryfikujących spełnienie wymagań
7.1.8 Przeprowadzanie testów

Zarządzanie 7.2.3 Planowanie zarządzania konfiguracją


konfiguracją 7.2.4 Identyfikowanie elementów konfiguracji
oprogramowania 7.2.5 Kontrolowanie konfiguracji
7.2.6 Zarządzanie statusem konfiguracji

Opracowywanie 7.3.3 Sporządzanie planu dokumentacji


dokumentacji 7.3.4 Implementowanie dokumentacji
7.3.5 Tworzenie i dystrybuowanie dokumentacji

Szkolenie 7.4.3 Planowanie programu szkoleń


7.4.4 Opracowywanie materiałów szkoleniowych
7.4.5 Walidacja programu szkoleń
7.4.6 Implementowanie programu szkoleń

Weryfikacja to badania zgodności modeli systemu ze specyfikacją w ramach przeglą-


dów, audytów i inspekcji kodu. Walidacja to upewnianie się, że system istotnie trafia w po-
trzeby użytkowników, co jest przedmiotem alfa- i beta-testów oraz testów akceptacyjnych
klienta. Proces weryfikacji i walidacji przewija się przez cały cykl życiowy, w celu jak najwcze-
śniejszego wykrywania wszelkich anomalii w realizacji projektu. Przykładowo każdy model,
po zakończeniu jego tworzenia, powinien być przeglądnięty zgodnie ze stosowną listą kontrolną.
Efektem przeglądu danego modelu (na przykład modelu projektu systemu) może okazać się
konieczność zmodyfikowania modelu utworzonego w innymi procesie (na przykład modelu
analitycznego). Aktywność zbierania i analizowania rozmaitych metryk skutkuje kolekcją da-
15.3. Charakteryzowanie dojrzałości modeli cyklu życiowego 695

nych przydatną prawdopodobnie w przyszłych projektach i stanowiącą istotny element wiedzy


organizacyjnej. Planowanie i opracowywanie testów powinno rozpocząć się natychmiast po
skompletowaniu wymagań; w dużych projektach zadania te wykonywane są przez uczestni-
ków niebędących programistami. Ogólne mechanizmy przeprowadzania przeglądów, audytów
i inspekcji opisaliśmy w rozdziale 3. „Organizacja projektu i komunikacja", zaś przeglądy
związane ze zbieraniem wymagań, ich analizą i projektowaniem systemu — odpowiednio
w rozdziałach 4., 5. i 6. Aktywnościom testowania poświęciliśmy rozdział 11.
Zarządzanie konfiguracją oprogramowania to śledzenie i kontrolowanie zmian wpro-
wadzanych do produktów. Do takich kontrolowanych produktów zaliczają się między innymi
kod źródłowy systemu, wszystkie jego modele, plan zarządzania projektem i wszelkie inne
dokumenty widoczne dla uczestników projektu. Zarządzanie konfiguracją było tematyką
rozdziału 13.
Dokumentowanie to proces szczegółowego opisywania poszczególnych produktów
(z wyjątkiem kodu źródłowego). Poszczególne dokumenty sporządzane są według ustalonych
szablonów, jednakże standard IEEE 1074 nie definiuje żadnego takiego szablonu czy też do-
kumentu wzorcowego. Proces dokumentowania jest procesem integralnym i specyficznym
dla konkretnego etapu produktu. W książce tej przedstawiliśmy różne przykłady dokumentów,
między innymi dokument analizy wymagań (RAD) w rozdziale 5., dokument projektu systemu
(SDD) w rozdziale 9., dokument projektu obiektów (ODD) w rozdziale 10. i plan zarządzania
projektem (SPMP) w rozdziale 14.

15.3. Cha ra kte ryżowa nie dojrzałości modeli cyklu życiowego


W poprzedniej sekcji opisaliśmy skrótowo aktywności i artefakty składające się na cykl ży-
ciowy oprogramowania, zgodnie ze standardem IEEE 1074. Standard ten nie precyzuje jed-
nak, które z nich powinny zostać wybrane dla konkretnego projektu. Wskazówek użytecznych
przy tym wyborze dostarcza model dojrzałości organizacyjnej (CMM — Capability Maturity
Model), którego istotę stanowi założenie, iż tworzenie systemów informatycznych staje się
bardziej przewidywalne, gdy ujęte zostaje w formę strukturalnego cyklu życiowego, jawnego
i zrozumiałego dla wszystkich uczestników oraz zdolnego do radzenia sobie z nieuchronnymi
zmianami. CMM wyróżnia pięć następujących poziomów dojrzałości organizacji cyklu życio-
wego, o czym piszą M. C. Paulk, C. V. Weber i B. Curtis [Paulk i in., 1995].

Poziom 1: Początkowa (Initial). Poziom ten oznacza konglomerat działań ad hoc, których
zdefiniowanie w formie aktywności jest raczej ewenementem niż regułą. Powodzenie tych
zadań zależy przede wszystkim od heroicznego wysiłku i niezwykłych umiejętności uczestni-
ków. Z perspektywy klienta cykl życiowy oprogramowania, jeżeli w ogóle istnieje jako taki,
jest przysłowiową czarną skrzynką: po uzgodnieniu umowy projektu klient musi poczekać
do końca na możliwość oceny jakiegokolwiek produktu, bowiem w trakcie realizacji projektu
nie ma żadnej możliwości współuczestnictwa w jego zarządzaniu.

Poziom 2: Powtarzalna (Repeatable). Każdy projekt charakteryzuje się dobrze zdefiniowanym


modelem cyklu życiowego, jednakże modele te różnią się w poszczególnych projektach, co
poważnie ogranicza możliwość wielokrotnego wykorzystywania zdobywanej wiedzy i doświad-
czenia. Podstawowe procesy zarządzania mają na celu kontrolowanie kosztu i harmonogramów.
696 Rozdział 15. • Cykl życiowy oprogramowania

Nowe projekty realizowane są w oparciu o doświadczenia organizacyjne zdobyte przy pro-


jektach z pokrewnych dziedzin aplikacyjnych. Wpływ klienta na realizację projektu ograni-
czony jest do ściśle zdefiniowanych punktów czasowych, przede wszystkim przeglądów
klienckich i testów akceptacyjnych.

Poziom 3: Zdefiniowana (Defined). Poziom ten charakteryzuje się dokumentowaniem wszyst-


kich menedżerskich i technicznych aktywności w ramach organizacji; na początku każdego
projektu tworzony jest specyficzny model jego cyklu życiowego. Klientowi znany jest za-
równo model standardowy, jak i jego odmiana dla konkretnego projektu.

Poziom 4: Zarządzana (Managed). Dla aktywności i produktów zdefiniowane są metryki, któ-


rych ciągłe kolekcjonowanie umożliwia analizowanie i rozumienie cyklu życiowego w katego-
riach ilościowych. Metryki te znane są klientowi, który dodatkowo informowany jest o stopniu
ryzyka projektu przed jego rozpoczęciem.

Poziom 5: Zoptymalizowana (Optimized). Dane pochodzące z różnych metryk wykorzy-


stywane są w mechanizmach sprzężenia zwrotnego, z myślą o doskonaleniu modeli cyklu ży-
ciowego w miarę realizacji kolejnych projektów.

Aby możliwe było klasyfikowanie konkretnej organizacji w kategoriach powyższego


modelu, instytut SEI4 zdefiniował zbiór tak zwanych kluczowych obszarów procesu (KPA
— Key Process Areas); niektóre z tych obszarów wykraczają poza aktywności definiowane
w IEEE 1074 (patrz tabela 15.7). Aby zademonstrować odpowiedni poziom dojrzałości,
organizacja musi wykazać się zgodnością ze wszystkimi KPA zdefiniowanymi dla tegoż
poziomu.

Tabela 15.7. Kluczowe obszary procesu charakteryzujące poszczególne poziomy dojrzałości organi-
zacyjnej

Poziom dojrzałości Kluczowe obszary procesu


1. Początkowa Brak

2. Powtarzalna Cel: ustanowienie podstawowej kontroli w zarządzaniu projektem.


• Zarządzanie wymaganiami: wymagania otrzymują status linii bazowej
w umowie projektu i utrzymywane są przez cały czas realizacji projektu.
• Planowanie i śledzenie projektu: na początku projektu definiowany
jest plan zarządzania projektem; jest on kontrolowany i aktualizowany
w czasie realizacji projektu.
• Zarządzanie podwykonawcami: organizacja wybiera i kontroluje
dostawców k o m p o n e n t ó w wysokiej jakości.
• Zarządzanie zapewnieniem jakości: wszystkie produkty i aktywności
procesu są p r z e d m i o t e m przeglądów i audytu w celu zapewnienia,
że odpowiadają standardom i wytycznym przyjętym w organizacji.
• Zarządzanie konfiguracją: zbiór elementów konfiguracji zostaje
zdefiniowany i kontrolowany jest przez cały czas realizacji projektu.

4
Patrz http://pl.wikipedia.org/wiki/Software_Engineering_Institute — przyp. tłum.
15.3. Charakteryzowanie dojrzałości modeli cyklu życiowego 697

Tabela 15.7. Kluczowe obszary procesu charakteryzujące poszczególne poziomy dojrzałości organi-
zacyjnej — ciąg dalszy

Poziom dojrzałości Kluczowe obszary procesu


3. Zdefiniowana Cel: zbudowanie infrastruktury umożliwiającej stosowanie pojedynczego
modelu cyklu życiowego do wszystkich projektów.
• Cel procesu organizacyjnego: w organizacji wydzielony jest stały
zespół do utrzymywania i doskonalenia modelu cyklu życiowego.
• Definicja procesu organizacyjnego: standardowy model cyklu życiowego
stosowany jest do wszystkich projektów realizowanych przez organizację;
informacje i dokumentacja, związane z cyklem życiowym, utrzymywane
są w formie bazy danych.
• Programy szkolenia: organizacja identyfikuje własne potrzeby w zakresie
szkoleń na potrzeby poszczególnych projektów i opracowuje programy
szkoleniowe.
® Zintegrowane zarządzanie oprogramowaniem: każdy zespół zaangażowany
w realizację projektu jest w stanie wyprowadzić specyficzne dla siebie
procesy z procesu standardowego dla organizacji.
• Inżynieria produktów programistycznych: oprogramowanie tworzone
jest w zgodzie ze z d e f i n i o w a n y m cyklem życiowym, przy użyciu
określonych metodologii i narzędzi.
• Komunikacja międzyzespołowa: poszczególne zespoły współpracują
w celu rozwiązywania problemów i negocjowania wymagań.
• Przeglądy partnerskie: programiści nawzajem kontrolują i oceniają
swoje produkty w celu wykrycia usterek oraz koniecznych zmian.
4. Zarządzana Cel: ilościowe postrzeganie aktywności i produktów cyklu życiowego.
• Ilościowe zarządzanie projektem: metryki dotyczące produktywności
i jakości są zdefiniowane i na bieżąco zbierane w ramach całego projektu.
Nie powinny być wykorzystywane w sposób bezpośredni, na przykład
do oceny produktywności programistów, lecz przechowywane w bazie
danych w celu dokonywania porównań z innymi projektami.
• Zarządzanie jakością: na poziomie organizacji zdefiniowany jest zbiór
celów jakościowych dla produktów. Spełnienie tych celów jest na bieżąco
monitorowane, tak by klient otrzymywał produkty wysokiej jakości.
5. Optymalizowana Cel: śledzenie zmian technologicznych i procesowych, w celu określenia
ich ewentualnego wpływu na konieczność zmian w ramach modeli lub
produktów, także w czasie realizacji projektu.
• Zapobieganie usterkom: usterki popełnione w poprzednich projektach
są analizowane na podstawie dostępnych metryk i w razie potrzeby
podejmowane są działania zapobiegające popełnianiu tych samych błędów.
• Zarządzanie zmianami technologicznymi: nowinki technologiczne
i innowacje są na bieżąco śledzone, a związana z tym i n f o r m a c j a
upowszechniana jest na f o r u m organizacji.
• Zarządzanie zmianami procesu: procesy składające się na realizację
projektu są nieustannie doskonalone w celu niwelowania nieefektywności
ujawnianych na podstawie zbieranych metryk. Ciągłe zmiany stanowią
normę, nie wyjątki.
698 Rozdział 15. • Cykl życiowy oprogramowania

15.4. Modele cyklu życiowego


Na rysunku 15.7 ukazany jest przepływ informacji między procesami definiowanymi przez
standard IEEE 1074; przedstawiony na rysunku diagram klas UML zaczerpnięty został z pracy
[IEEE Std. 1074-2006]. Każde skojarzenie między dwoma procesami może być identyfikowane
z produktem, wytwarzanym przez jeden proces i konsumowanym przez drugi. Skojarzenie
takie reprezentuje ponadto formalny kanał komunikacji między uczestnikami projektu, wy-
mieniającymi między sobą dokumenty, modele lub kod źródłowy.

Rysunek 15.7. Powiązania między procesami standardu IEEE 1074. Jak łatwo spostrzec, zależności
między procesami i aktywnościami są dość skomplikowane, co praktycznie eliminuje możliwość
wykonywania tych procesów w sposób ściśle sekwencyjny

W kolejnej sekcji przyjrzymy się kilku wybranym modelom cyklu życiowego. Większość z nich
koncentruje się na procesach realizacyjnych.
15.4. Modele cyklu życiowego 699

15.4.1. Sekwencyjne modele Ukierunkowane na aktywności


Rozpoczniemy od m o d e l i u k i e r u n k o w a n y c h na aktywności. Najstarszym z nich jest m o d e l
kaskadowy, zakładający sekwencyjne następstwo poszczególnych aktywności. Jego o d m i a n ą
jest V-model, wprowadzający zróżnicowanie poziomów abstrakcji między tymi aktywnościami.

Model kaskadowy

Model kaskadowy (waterfall model), opisany po raz pierwszy w publikacji W . W . Royce'a


[Royce, 1970], jest m o d e l e m zakładającym sekwencyjne wykonywanie procesów programi-
stycznych i menedżerskich, wywodzących się spośród procesów opisywanych w poprzedniej
sekcji (patrz rysunek 15.8, diagram klas zaczerpnięty został z pracy W . W . Royce'a ).

Rysunek 15.8. Model kaskadowy tworzenia oprogramowania. Aktywności związane z wytwarzaniem


oprogramowania realizowane są sekwencyjnie. Nazwy procesów i aktywności zgodne są z standardem
IEEE 1074; dla prostoty pominęliśmy procesy zarządzania projektem i procesy integralne

Całość obsługi wymagań zostaje zakończona przed rozpoczęciem projektowania systemu.


Podstawowym założeniem modelu jest niepowracanie do zakończonych aktywności. Kluczową
właściwością modelu kaskadowego jest ciągła aktywność weryfikacyjna (nazywana przez Royce'a
„krokiem weryfikacyjnym" — verification step), mająca na celu zapewnienie, że żadna z ak-
tywności programistycznych nie wprowadza niepożądanych wymagań ani rezygnacji z któregoś
700 Rozdział 15. • Cykl życiowy oprogramowania

z obowiązujących. Otrzymujemy w ten sposób prosty (a raczej — uproszczony) widok budo-


wania oprogramowania jako transformacji wymagań, krok po kroku, na kod źródłowy apli-
kacji; postęp tej transformacji mierzony jest liczbą zakończonych zadań.

V-Model

V-model jest odmianą modelu kaskadowego, uwidaczniającą zależności między aktyw-


nościami programistycznymi a weryfikacyjnymi, znajdującymi się na tym samym poziomie
abstrakcji (poziomy abstrakcji poszczególnych aktywności są tym elementem, który odróżnia
V-model od modelu kaskadowego). Wszystkie aktywności, w kierunku od zbierania wymagań
do implementowania systemu, zmierzają do stopniowego tworzenia coraz bardziej szczegó-
łowej reprezentacji systemu, podczas gdy wszystkie aktywności układające się w kierunku prze-
ciwnym koncentrują się na walidacji systemu. Na rysunku 15.9 V-modeł przedstawiony został
w formie diagramu aktywności, zaczerpniętego z książki R. W. Jensena i C. C. Toniesa [Jensen
i Tonies, 1979].

Rysunek 15.9. V-model tworzenia oprogramowania. Poziome przepływy obiektów reprezentują ka-
nały przepływu informacji między aktywnościami na tym samym poziomie abstrakcji. Zachowali-
śmy tradycyjny kształt litery V oryginalnego diagramu, jednakże w języku UML taki czy inny kształt
diagramu nie ma żadnego znaczenia semantycznego

Jak nietrudno się zorientować, wyższe poziomy abstrakcji V-modelu związane są z wy-
maganiami, pod względem ich zbierania i realizowania podczas eksploatacji systemu. Środ-
kowe poziomy abstrakcji koncentrują się na odwzorowywaniu problemów w architekturę
oprogramowania, zaś poziomy najniższe dotyczą szczegółów w postaci tworzenia kompo-
15.4. Modele cyklu życiowego 701

nentów i łączenia ich ze sobą — i tak na przykład celem aktywności „Testowanie jednostkowe"
jest walidacja jednostek pod względem ich zgodności z opisem w projekcie szczegółowym, zaś
aktywność „Integrowanie i testowanie komponentów" ma za zadanie analogiczną walidację
komponentów funkcjonalnych projektu wstępnego (wysokopoziomowego).
Model kaskadowy i jego odmiany to pod wieloma względami uproszczona abstrakcja
procesu tworzenia oprogramowania. Podstawową słabością wszystkich tych modeli jest (nie
zawsze realne) założenie, że gdy dana aktywność zostanie zakończona, a jej produkty zweryfi-
kowane, można objąć je statusem linii bazowej i przejść do kolejnych aktywności. Z sytuacją
zbliżoną do tego ideału mamy jednak do czynienia tylko wówczas, gdy specyfikacja wymagań
jest bardzo wyraźna i stabilna, co w praktyce zdarza się dość rzadko; częściej jest tak, że reali-
zacja danej aktywności identyfikuje konieczność zmian w produktach będących owocem po-
przednich aktywności.

15.4.2. Iteracyjne modele ukierunkowane na aktywności


Model spiralny oraz (nowszy) model jednolitego procesu to modele bazujące na iteracyjnym
wykonywaniu poszczególnych aktywności.

Model spiralny

Model spiralny B. Boehma [Boehm, 1987] to model cyklu życiowego ukierunkowa-


ny na aktywności, zaprojektowany z intencją zniwelowania podstawowej wady modelu ka-
skadowego, jaką jest mało realne założenie o sporadyczności zmian w wytworzonych już pro-
duktach. Model spiralny oparty jest na tych samych aktywnościach, co model kaskadowy,
dodaje jednak do każdej kilka pomocniczych, takich jak zarządzanie ryzykiem, wielokrotne
wykorzystywanie rozwiązań i prototypowanie; te pomocnicze aktywności nazywane są cyklami
(cycles) lub rundami (rounds). Model spiralny koncentruje się na zmaganiach z ryzykiem
w sposób przyrostowy, zależnie od priorytetu jego czynników. Każda runda składa się z czte-
rech faz (patrz rysunek 15.10). W pierwszej fazie (zlokalizowanej w górnej lewej ćwiartce
płaszczyzny) programiści eksplorują możliwe rozwiązania, definiują ograniczenia i iden-
tyfikują cele. W fazie drugiej (górna prawa ćwiartka) programiści zmagają się z ryzykiem, ja-
kim obarczone są rozwiązania zaproponowane w pierwszej fazie. W fazie trzeciej (reprezento-
wanej przez dolną prawą ćwiartkę) programiści tworzą i weryfikują prototypy oraz wykonują
inne czynności specyficzne dla najbardziej ryzykownych części systemu, wreszcie faza czwarta
(odzwierciedlona przez dolną lewą ćwiartkę) koncentruje się na planowaniu kolejnej rundy
na podstawie dotychczasowych rezultatów rundy bieżącej. Ostatnia faza rundy jest zwykle
przeprowadzana w postaci przeglądu z udziałem programistów, klienta i użytkowników.
Przedmiotem tych przeglądów są produkty wytworzone w bieżącej fazie i fazach poprzed-
nich. Model spiralny wyróżnia rundy, takie jak koncepcja operacji, przetwarzanie wymagań,
projektowanie produktu, projektowanie szczegółowe, kodowanie, testowanie jednostkowe, inte-
growanie i testowanie integracyjne, testowanie akceptacyjne i implementowanie 5 .

5
Na rysunku pokazaliśmy szczegółowo tylko trzy pierwsze z wymienionych rund, pozostałe repre-
zentowane są jedynie w postaci bloków w zewnętrznej warstwie spirali.
702 Rozdział 15. • Cykl życiowy oprogramowania

Rysunek 15.10. Model spiralny Boehma (na podstawie książki B. Boehma [Boehm, 1987]). Odle-
głość od początku reprezentuje zakumulowany koszt projektu. Kąt z lewą półosią reprezentuje typ
aktywności — przykładowo w projekcie PI realizowana jest aktywność analizowania ryzyka związa-
nego z wymaganiami, natomiast projekt P2 znajduje się na etapie projektowania systemu

Każda runda wzoruje się na modelu kaskadowym i obejmuje następujące aktywności:

1. Określenie celów,
2. Zdefiniowanie ograniczeń,
3. Rozpatrzenie możliwości realizacyjnych,
4. Identyfikowanie ryzyka,
5. Minimalizowanie ryzyka,
6. Opracowanie i zweryfikowanie produktu dla następnego poziomu,
7. Planowanie.

Dwie pierwsze aktywności to definiowanie problemu rozwiązywanego w bieżącym cyklu,


trzecia jest definiowaniem przestrzeni rozwiązań, dwie następne sprowadzają się do przewi-
dywania czynników ryzyka, mogącego przekładać się na wysokie koszty lub nawet groźbę ska-
sowania projektu. Aktywność szósta to fizyczna realizacja cyklu, ostatnia zaś jest aktywnością
menedżerską, przygotowującą następny cykl. Rundy te można przedstawić w postaci układu
współrzędnych biegunowych, tak jak na rysunku 15.10; pierwsza runda rozpoczyna się w górnej
15.4. Modele cyklu życiowego 703

lewej ćwiartce tego układu, kolejne natomiast, w kierunku ruchu wskazówek zegara, nakładają
się kolejnymi warstwami po spirali. Współrzędne biegunowe umożliwiają łatwe określenie
statusu projektu w dowolnym momencie jego realizacji: rosnąca odległość rundy od środka
spirali reprezentuje akumulujący się koszt projektu, zaś odległość kątowa od lewej półosi wska-
zuje stan zaawansowania każdej fazy.

Jednolity proces wytwarzania oprogramowania

Jednolity proces wytwarzania oprogramowania (Unified Software Development Pro-


cess lub Unified Process), zwany krótko jednolitym procesem to model cyklu życiowego za-
proponowany przez G. Boocha, I. Jacobsona i J. Rumbaugha [Jacobson i in., 1999]. W całym
czasie życia oprogramowania model ten rozróżnia istotne zakresy czasu zwane cyklami; nie
mają one jednak wiele wspólnego z cyklami modelu spiralnego, mogą być postrzegane raczej
jako charakterystyczne etapy dojrzałości systemu w całym okresie jego życia, co zresztą od-
zwierciedlają ich zwyczajowe nazwy — kolejno „narodziny" (birth), „dzieciństwo" (childhood),
„dorosłość" (adulthood), „emerytura" (retirement) i „zgon" (death) — „zwyczajowe", bo model
nie definiuje oficjalnie żadnych zasad nazywania cykli. Cykl generalnie kończy się wraz z prze-
kazaniem systemu klientowi. Każdy cykl dzieli się na cztery fazy, odzwierciedlające jego po-
szczególne stany i nazwane (kolejno) Inception („prapoczątek"), Elaboration („opracowywanie"),
Construction („tworzenie") i Transition („przenoszenie") (patrz rysunek 15.11). W fazie In-
ception ustalany jest zakres systemu, czyli określenie elementów, które powinien i których
nie powinien zawierać. Zwykle identyfikowane są wtedy także przypadki użycia, stanowiące
podstawę rozstrzygania kompromisów między sprzecznymi celami projektowymi — przypadki
takie nazywane są rdzennymi przypadkami użycia (core use cases). Definiowana jest także
przynajmniej jedna propozycja architektury oprogramowania, w kontekście której wspomniane
przypadki użycia zostają zademonstrowane. Faza kończy się skonstruowaniem pierwszego
przybliżenia harmonogramu i budżetu projektu, przy czym oszacowania dotyczące fazy Ela-
boration są bardziej szczegółowe niż w przypadku dalszych faz.

Rysunek 15.11. Stany (fazy) systemu w ujęciu jednolitego procesu

W fazie Elaboration wymagania widziane oczami użytkownika zapisywane są w repo-


zytorium. Następuje zaprojektowanie architektury oprogramowania i dokonanie wstępnego
wyboru między tworzeniem własnych rozwiązań a zakupem gotowych „z półki", co z kolei
stanowi podstawę szacowania kosztów, terminów i niezbędnych zasobów.
704 Rozdział 15. • Cykl życiowy oprogramowania

W fazie Construction dokonuje się zakupu i tworzenia komponentów, a wykonane emisje


porównywane są z kryteriami akceptacyjnymi.
Faza Transition rozpoczyna się wówczas, gdy system staje się wystarczająco dojrzały do
przekazania go użytkownikom.
Każda z faz składa się z pewnej liczby iteracji, każda zaś iteracja dedykowana jest okre-
ślonej grupie powiązanych przypadków użycia albo minimalizowaniu określonego czynnika
ryzyka. Iterację w rozumieniu niniejszego opisu można w przybliżeniu utożsamiać z pojęciem
projektu w takim sensie, w jakim opisywaliśmy go w rozdziale 14. „Zarządzanie projektem".
W czasie trwania każdej iteracji wykonywanych jest równolegle kilka aktywności. Dla
uwypuklenia tej równoległości oraz faktu, że mamy do czynienia z kompletną iteracją, aktyw-
ności te nazywane są przepływami pracy (workflows)6. Przepływy pracy zorientowane na two-
rzenie systemu nazywane są inżynierskimi (engineering workflows) i dzielą się na cztery kategorie,
związane z: przetwarzaniem wymagań (Requirements), projektowaniem (Design), implemen-
tacją (Implementation) i testowaniem (Test) (patrz rysunek 15.12). Zostały one opisane we
wcześniejszych rozdziałach tej książki: kategoria Requirements obejmuje analizowanie dzie-
dziny aplikacyjnej i tworzenie związanych z tą analizą artefaktów, między innymi modelu
analitycznego (patrz rozdział 5.); kategoria Design koncentruje się na tworzeniu rozwiązań
i projektowaniu artefaktów, czego owocami są między innymi model projektu systemu
(patrz rozdział 6.) i model projektu obiektów (patrz rozdział 8.). Kategoria Implementation to
tworzenie kodu źródłowego (patrz rozdział 10.), a kategoria Test to testowanie modeli, kodu
źródłowego i innych artefaktów przeznaczonych do wdrażania (patrz rozdział 11.).

Rysunek 15.12. Przepływy pracy w ramach jednolitego procesu [Royce, 1998]

Jednolity proces rozróżnia cztery aktywności międzyfunkcyjne, nazywane pomocniczymi


przepływami pracy (supporting workflows) i dotyczące zarządzania (Management), środowiska
(Environment), oceny (Assesment) i wdrażania (Deployment). Pierwsza z tych aktywności obej-

6
Jednolity proces stosuje własne konwencje nazewnicze, nie wzorując się na standardzie IEEE. Prze-
pływy pracy mogą być utożsamiane ze specjalnymi aktywnościami rozciągającymi się na więcej niż jedną
iterację. Inżynieryjne przepływy pracy mogą być uważane za funkcje projektowe, ponieważ czas ich
trwania pokrywa się z czasem trwania iteracji, natomiast pomocnicze przepływy pracy stanowią od-
powiedniki procesów integralnych standardu IEEE 1074.
15.4. Modele cyklu życiowego 705

muje planistyczne aspekty systemu, opisywane w rozdziale 14., druga koncentruje się wokół
samego środowiska programistycznego i automatyzowania wykonywanych w jego ramach
czynności, jak również utrzymywania tego środowiska w aktualnym stanie, szczególnie w aspek-
cie infrastruktury komunikacyjnej i zarządzania konfiguracją. Trzecia wiąże się z ocenianiem
procesów i produktów, czyli z przeglądami, wędrówkami po kodzie i inspekcjami kodu. Przed-
miotem czwartej jest przekazywanie systemu docelowym użytkownikom.
Jednolity proces uwydatnia etapowość przydziału zasobów dla każdego przepływu pracy
w danej fazie lub iteracji, wiążąc w ten sposób bardzo ściśle problemy zarządzania projektem
z innymi problemami cyklu życiowego programowania (patrz rysunek 15.13). Dzieje się tak
dlatego, że każda iteracja traktowana jest jak osobno zarządzany projekt (patrz rozdział 14.).
Ponieważ czas trwania przepływu pracy jest tożsamy z czasem trwania projektu, mogą one być
uważane za funkcje projektowe, dedykowane zróżnicowanym potrzebom poszczególnych faz.
Przykładowo w fazie Elaboration największe zapotrzebowanie na zasoby przejawia przepływ
Requirement, jednakże w fazie Construction ciężar głównego zapotrzebowania na zasoby
przesuwa się na przepływy Design i Implementation.

Rysunek 15.13. Siedem przepływów pracy w ramach jednolitego procesu — Management, Environment,
Requirements, Design, Implementation, Assessment i Deployment — to funkcje projektowe trwające
przez cały czas życia systemu. Zapotrzebowanie na zasoby zmienia się dla poszczególnych przepływów
pracy wraz ze zmianą fazy i iteracji. Histogram dla każdego przepływu wskazuje ilość zasobów potrzebną
dla iteracji — im wyższy blok, tym więcej konsumowanych zasobów (wykres na podstawie prac I. Jacob-
sona, G. Boocha i J. Rumbaugha [Jacobson i in., 1999] oraz W. W. Royce'a [Royce, 1998])

Na rysunku 15.14 przedstawiamy widok jednolitego procesu z perspektywy zbioru


modeli. Wymagania reprezentowane są przez model przypadków użycia, zaś model analityczny
opisuje system w podziale na klasy. Model projektu systemu definiuje strukturę systemu w kate-
goriach podsystemów i ich interfejsów, natomiast model wdrażania definiuje sposób dystry-
bucji systemu. Zadaniem modelu implementacji jest odwzorowanie klas w komponenty, wreszcie
model testowania służy do zapewnienia, że wykonywany system spełnia wymagania funkcjo-
nalne reprezentowane przez model przypadków użycia.
706 Rozdział 15. • Cykl życiowy oprogramowania

Rysunek 15.14. Widok jednolitego procesu w ukierunkowaniu na encje. Zależności między encjami
— odzwierciedlające zależnościom między modelami — oznaczają identyfikowalność modeli. Dla
przejrzystości ograniczyliśmy się do powiązań modelu przypadków użycia z innymi modelami

Modele powiązane są relacjami identyfikowalności: każdy element modelu może być


odniesiony do przynajmniej jednego elementu w powiązanym modelu. Przykładowo każdy
przypadek użycia daje się powiązać z przynajmniej jedną klasą modelu analitycznego. Relacje
identyfikowalności (patrz sekcja 4.3.4) ułatwiają zrozumienie wpływu zmian wprowadzanych
do jednego modelu na inne modele, między innymi wpływu zmian w wymaganiach na pozostałe
modele systemu.
Zależności między modelami widoczne na rysunku 15.14 mogą być wykorzystywane na
wiele różnych sposobów. W ramach inżynierii postępującej (forward engineering) model ana-
lityczny i model projektu systemu budowane są na bazie modelu przypadków użycia i służą do
generowania modelu implementowania i modelu testowania. W ramach inżynierii odwraca-
jącej (reverse engineering) model analityczny i model projektowy odtwarzane są na podstawie
istniejącego kodu. Kombinacją obu tych podejść jest inżynieria wahadłowa (round-trip engi-
neering), umożliwiająca programiście swobodne przełączanie się między poszczególnymi
modelami, zależnie od tego, który z nich staje się obiektem najdalej posuniętych zmian.

15.4.3. Modele ukierunkowane na encje


Jeśli czas między kolejnymi zmianami nie jest znacząco większy od czasu trwania aktywności
i jeśli wspomniane zmiany dotyczą zarówno dziedziny aplikacyjnej, jak i dziedziny realizacyjnej,
wykorzystywanie tak modelu kaskadowego, jak modelu spiralnego staje się problematyczne.
Załóżmy bowiem, że linia rozwojowa platformy sprzętowej, którą wybrali programiści, cha-
rakteryzuje się nową wersją co trzy - cztery lata, to przy projekcie trwającym trzy lata wiedza
programistów, jaką posiadają na początku realizacji projektu, okaże się wystarczająca do
właściwego wyboru tejże platformy na etapie projektowania systemu. W przypadku jed-
nak ukazywania się nowej wersji platformy co trzy - cztery miesiące i tak samo długo trwają-
cego projektu zmiana wybranej platformy w trakcie realizacji tego projektu jest wielce
prawdopodobna.
15.4. Modele cyklu życiowego 707

By zapobiegać tego typu problemom, skonstruowano model ukierunkowany na encje,


zwany zagadnieniowym modelem cyklu życiowego (issue-based life cycle model), opierający
się — jak wskazuje sama nazwa — na racjonalizacji systemu kryjącej się za jego modelem za-
gadnień (patrz rozdział 12. „Zarządzanie racjonalizacją"). Każdy projekt rozpoczyna się od
sformułowania relatywnych dla niego zagadnień: w przypadku tworzenia systemu od zera
zagadnienia te wypływają zwykle z doświadczenia menedżera lub wzorowane są na standar-
dowym szablonie, w przypadku inżynierii wtórnej zagadnienia te są wprost dostępne w ra-
cjonalizacji dotychczas eksploatowanego systemu. Gdy projekt ma długą historię, jego racjo-
nalizacja też jest zwykle odpowiednio obszerna i można w niej napotkać zagadnienia
w rodzaju: Jak liczebny powinien byt początkowo zespół programistów? Czy mechanik powi-
nien mieć dostęp do informacji specyficznych dla kierowcy? Jakie będzie najodpowiedniejsze
middleware? Jakiego typu architektury powinniśmy użyć? W jakim języku programowania
powinniśmy tworzyć kod źródłowy? Takie i inne zagadnienia związane z racjonalizacją po-
winny być przechowywane w bazie dostępnej dla wszystkich uczestników.
Dla przypomnienia: zagadnienie może być otwarte lub zamknięte. Zamknięte zagad-
nienie to takie, dla którego znaleziono rozstrzygnięcie. I tak zamknięte jest zagadnienie wyboru
platformy systemowej, dla którego rozstrzygnięciem jest wybór systemu Solaris. Zamknięte
zagadnienia mogą być jednak ponownie otwierane, gdy wymagać tego będą zmiany w dzie-
dzinie aplikacyjnej lub dziedzinie realizacyjnej; jeśli przykładowo okaże się, że tworzony wła-
śnie system może być przydatny także szerokiej rzeszy użytkowników Windows czy Linuksa,
zagadnienie wyboru platformy docelowej znów staje się otwarte. Otwarte zagadnienia roz-
strzygane są w drodze dyskusji lub negocjacji między uczestnikami projektu (patrz rozdział 3.
„Organizacja projektu i komunikacja"). Mówimy, że zagadnienie i 2 zależne jest od zagad-
nienia i 1, jeśli rozstrzygnięcie zagadnienia i 1 zawiera ewentualności właściwe dla rozstrzy-
gnięcia i 2. Śledzenie zależności między zagadnieniami umożliwia programistom ocenę poten-
cjalnych konsekwencji otwarcia określonego zagadnienia. W bazie przechowującej zagadnienia
znajduje się także informacja o zależnościach między nimi. Na rysunku 15.15 widoczny jest
wycinek takiej bazy.

Rysunek 15.15. Wycinek bazy zagadnień projektu. Zagadnienia i 1 i i 5 zostały rozstrzygnięte, podczas
gdy wszystkie inne zagadnienia pozostają otwarte. Zależności między zagadnieniami wskazują na fakt, że
rozstrzygnięcie wybrane dla jednego zagadnienia może wiązać się z ograniczeniami ewentualności roz-
wiązań dla innych zagadnień
708 Rozdział 15. • Cykl życiowy oprogramowania

Zagadnienia mogą być odwzorowywane w aktywności opisywanych wcześniej modeli


cyklu życiowego. Załóżmy na przykład, że w skład modelu wchodzą aktywności planowania
i projektowania systemu: wówczas zagadnienie Jak liczebny powinien być początkowo zespół
programistów? wyraźnie odnosi się do pierwszej z tych aktywności, podczas gdy zagadnienie
Jakiego typu architektury powinniśmy użyć? jest już zagadnieniem ściśle projektowym. W tym
kontekście status zagadnienia może przesądzać o statusie całej aktywności — aktywność pro-
jektowania systemu nie może być uznana za zakończoną, jeśli zagadnienie wyboru architektury
dla tego systemu pozostaje wciąż otwarte. Zauważmy, że opisywane wcześniej modele cyklu
życiowego mogą być uważane za szczególne przypadku modelu bazującego na zagadnieniach:
przykładowo, zgodnie z modelem kaskadowym, programiści w sposób ostateczny rozstrzygają
wszystkie zagadnienia związane z bieżącą aktywnością przed rozpoczęciem aktywności na-
stępnej. Ujęcie modelu kaskadowego w kategoriach modelu bazującego na zagadnieniach,
w kontekście aktywności projektowania systemu, widoczne jest na rysunku 15.16.

Rysunek 15.16. Model kaskadowy jako przypadek szczególny modelu bazującego na zagadnieniach.
Wszystkie zagadnienia należące do tej samej kategorii zawarte są w tym samym pakiecie UML. Za-
gadnienia związane ze zbieraniem wymagań i ich analizą zostały już zamknięte, przez co aktywności
zbierania wymagań i ich analizowania można uznać za zakończone

W przypadku modelu spiralnego Boehma ryzyko związane z otwieranymi ponownie


zagadnieniami szacowane jest na początku każdej rundy; zagadnienia rozstrzygane są w kolej-
ności malejących priorytetów, przypisanych na etapie analizy ryzyka. Zwróćmy jednak uwagę, iż
„zagadnienie" jest koncepcją bardziej ogólną niż „ryzyko", przykładowo zagadnienie Jaki model
kontroli dostępu powinniśmy zastosować? jest problemem projektowym, a nie czynnikiem ry-
zyka. W ogólnym przypadku (patrz rysunek 15.17) wszystkie aktywności mogą być powiązane
z otwartymi zagadnieniami, co oznacza, że aktywności te powinny być realizowane równolegle.
Celem menedżera projektu jest pozostawianie liczby otwartych zagadnień na jak najniższym
poziomie, niepowodującym ograniczeń dla rozstrzygania innych zagadnień. Właśnie wyko-
rzystywanie zagadnień i powiązań między nimi przy zarządzaniu aktywnościami cyklu ży-
ciowego pozwala na realizowanie wszystkich tych aktywności w sposób równoległy.
15.5. Literatura uzupełniająca 709

Rysunek 15.17. W skomplikowanym stanie projektu wszystkie aktywności mogą być powiązane z otwartymi
zagadnieniami, co oznacza, że wszystkie powinny być realizowane równolegle

15.5. Literatura uzupełniająca


Modele stanowiące odmianę modelu kaskadowego wciąż są w powszechnym użyciu. W latach
90. ubiegłego wieku od kompanii kontraktujących oprogramowanie dla Departamentu Obrony
USA żąda się zgodności jego tworzenia ze standardem 2167A [DoD-STD-2167A], bazującym
właśnie na modelu kaskadowym. Model „zębaty" (sawtooth model), opisany przez R. B. Rowena
[Rowen, 1990] stanowił propozycję ulepszenia V-modelu poprzez rozwiązanie problemu róż-
nicy punktów widzenia programisty i użytkownika na różnych poziomach abstrakcji.
Standard [ISO/IEC 12207,1995] to framework przeznaczony na potrzeby procesów cykli
życiowych, z dobrze zdefiniowaną terminologią, procesami, aktywnościami i zadaniami przy-
datnymi na potrzeby pozyskiwania, dostarczania, tworzenia, eksploatowania i pielęgnowania
oprogramowania. Standard ten jest niesłusznie uważany za następcę standardu IEEE 1074;
standard ISO/IEC 12207 jest wysokopoziomowym opisem aktywności przydatnych dla dwu-
stronnych relacji między dostawcą i odbiorcą oprogramowania; opisuje architekturę cykli ży-
ciowych, lecz nie precyzuje szczegółów implementowania czy wykonywania poszczególnych
aktywności. Standard IEEE 1074 plasuje się na niższym poziomie abstrakcji jako adresowany
do użytkowników zamierzających budować specyficzny cykl życiowy dla projektu lub grupy
projektów. Przykładowo IEEE 1074 specyfikuje zależności i interfejsy między aktywnościami
w kategoriach dokumentów i produktów. IEEE zaadaptowało ISO/IEC 12207, czego efektem
stał się standard IEEE/EIA 12207.0-1996. W 1997 roku standard IEEE 1074 został poszerzony
i zweryfikowany, wskutek czego obecnie oba standardy są zgodne ze sobą [IEEE/EIA, 1996].
Jednolity proces stał się standardem de facto dla iteratywnych modeli cyklu życiowego
ukierunkowanych na aktywności. Wywodzi on swą genealogię z metodyki Objectory opisanej
w pracach I. Jacobsona, M. Christersona, P. Jonssona i G. Overgaarda [Jacobson i in.,1992]
i I Jacobsona, M. Ericsona i A. Jacobsona [Jacobson i in., 1995]. Metodologia ta stała się pod-
stawą opracowania Rational Unified Process autorstwa P. Kruchtena [Kruchten, 1998],
który następnie uformował podstawy dla jednolitego procesu.
710 Rozdział 15. • Cykl życiowy oprogramowania

15.6. Ćwiczenia
15.1. Przedstaw aktywności z rysunku 15.2 i produkty z rysunku 15.4 na diagramie klas
UML, uwidoczniającym powiązanie między aktywnościami a produktami.
15.2. Załóżmy, że model kaskadowy przedstawiony na rysunku 15.8 został wyprowadzony
ze standardowego modelu widocznego na rysunku 15.7 w ramach aktywności mo-
delowania cyklu życiowego. Jakich procesów i aktywności brakuje w tym kontekście
w modelu kaskadowym.
15.3. Narysuj model spiralny Boehma, widoczny na rysunku 15.10 w postaci diagramu
aktywności UML. Porównaj czytelność tego diagramu z czytelnością oryginalnej spirali.
15.4. Narysuj diagram aktywności UML opisujący zależności między aktywnościami cyklu
życiowego, w którym przetwarzanie wymagań, projektowanie, implementowanie,
testowanie i pielęgnowanie systemu wykonywane są często (taki cykl życiowy okre-
ślany bywa mianem ewolucyjnego).
15.5. Wyjaśnij, w jaki sposób aktywności testowania mogą zostać poprawnie zainicjowane
przed rozpoczęciem aktywności implementacyjnych. Uzasadnij, dlaczego takie roz-
wiązanie jest pożądane.
15.6. W zarządzaniu projektem relacja między dwoma zadaniami jest zwykle inter-
pretowana jako relacja poprzedzania w czasie — jedno zadanie musi zostać zakoń-
czone, zanim będzie można rozpocząć drugie. W przypadku cyklu życiowego relacja
między dwiema aktywnościami jest zależnością między nimi — jedna aktywność
wykorzystuje produkty wytworzone przez drugą. Przedyskutuj tę różnicę i zilustruj ją
stosownym przykładem w kontekście V-modelu.
15.7. Wyobraź sobie, że jesteś członkiem komitetu IEEE rewidującego właśnie standard
IEEE 1074. Przydzielono Ci zadanie modelowania komunikacji jako jawnego procesu
integralnego. Przedstaw przykłady aktywności należących do tego procesu.

Bibliografia
B. Boehm A spiral model of software development and enhancement,
[Boehm, 1987]
„Software Engineering Project Management", str. 128 - 142, 1987.

DoD-STD-2167A Military Standard, Defense Systems Software


[DoD-STD-2167A]
Development, US Department of Defense, Washington, DC, 1988.

IEEE Standard for Developing Software Life Cycle Processes, IEEE


[IEEE Std. 1074-2006]
Computer Society, New York, 2006.
IEEE/EIA 12207.0-1996, Industry Implementation of International
[IEEE/EI A, 1996]
Standard ISO/IEC 12207: 1995 Standard for Information Technology
— Software life cycle processes, IEEE Computer Society & Electronic
Industries Association, marzec 1998.
[ISO/IEC 12207, 1995] ISO/IEC 12207, Information technology — Software life cycle processes,
I n t e r n a t i o n a l Organization for Standardization & I n t e r n a t i o n a l
Electrotechnical Commission, sierpień 1995.
Bibliografia 711

[Jacobson i in., 1992] I. Jacobson, M. Christerson, P. Jonsson, G. Overgaard Object-Oriented


Software Engineering — A Use Case Driven Approach, Addison-Wesley,
Reading, MA, 1992.
[Jacobson i in., 1995] I. Jacobson, M. Ericsson, A. Jacobson The Object Advantage: Business Process
Reengineering with Object Technology, Addison-Wesley, Reading, MA, 1995.
[Jacobson i in., 1999] I. Jacobson, G. Booch, J. Rumbaugh The Unified Software Development
Process, Addison-Wesley, Reading, MA, 1999.
[Jensen i Tonies, 1979] R. W. Jensen, C. C. Tonies Software Engineering, Prentice Hall, Englewood
Cliffs, NJ, 1979.
[Kruchten, 1998] P. Kruchten The Rational Unified Process: An Introduction, Addison-Wesley,
Reading, MA, 1998.
[Paulkiin., 1995] M. C. Paulk, C. V. Weber, B. Curtis (red.) The Capability Maturity
Model: Guidelines for Improving the Software Process, Addison-Wesley,
Reading, MA, 1995.
[Rowen, 1990] R. B. Rowen Software project management under incomplete and ambiguous
specifications, „IEEE Transactions on Engineering Management", t. 37,
nr 1, 1990.
[Royce, 1970] W. W. Royce „Managing the development of large software systems"
w: Tutorial: Software Engineering Project Management, IEEE Computer
Society, Washington, DC, str. 118 - 127, 1970.
[Royce, 1998] W. W. Royce, Software Project Management: A Unified Framework,
Addison-Wesley, Reading, MA, 1998.
16.1. Wstęp: pierwsze zdobycie K2 714

16.2. Środowisko projektu 717

16.3. Zagadnienia metodologiczne 719


16.3.1. Ile planowania? 719
16.3.2. Ile powtarzalności? 720
16.3.3. Ile modelowania? 721
16.3.4. Ile procesów cyklu życiowego? 723
16.3.5. Ile kontroli i monitorowania? 723
16.3.6. Kiedy przedefiniować cele projektu? 724
16.4. Spektrum metodologii 724
16.4.1. Metodologia Roycea 725
16.4.2. Programowanie ekstremalne (XP) 731
16.4.3. Metodologie rugby 737

16.5. Analizy przypadku 742


16.5.1. Projekt XP: ATRACT 743
16.5.2. Lokalny klient: FRIEND 746
16.5.3. Rozproszony projekt: JAMES 754
16.5.4. Podsumowanie analiz przypadku 761

16.6. Literatura uzupełniająca 766

16.7. Ćwiczenia 766

Bibliografia 767
Wszystko razem,
czyli metodologie
Powinniśmy być ostrożni w poleganiu wyłącznie na wiedzy wyniesionej
z doświadczenia — i niczym więcej: inaczej będziemy jak ten kot, który
nieopatrznie usiadł na gorącym piecu: już nigdy więcej nie usiądzie
na gorącym piecu (i bardzo dobrze), ale na zimnym też już nie.
— Mark Twain Following the Equator: A Journey Around the World

w poprzednich rozdziałach opisywaliśmy wiele różnych metod — programistycznych


i menedżerskich — związanych z różnymi aspektami złożonych i zmieniających się systemów
informatycznych. Dla zmagania się ze złożonością systemu i jego dziedziny aplikacyjnej wyko-
rzystywaliśmy między innymi modelowanie przypadków użycia, modelowania obiektów, style
architektoniczne, wzorce projektowe i odwzorowywanie modeli na kod źródłowy. Wiedzę
związaną z systemem przedstawialiśmy w formie diagramów UML, modeli zagadnień, mecha-
nizmów nieformalnej komunikacji i dokumentów. Z kolei w celu uporania się z częstymi
zmianami sięgaliśmy po różnorodne odmiany organizacji, procesów i cykli życiowych.
Czytelnicy, którzy mają za sobą tę lekturę, dysponują obecnie obrazem aktualnego stanu
sztuki inżynierii oprogramowania, który to obraz przyrównać można by do opasłej książki
kucharskiej. W praktyce jednak książka kucharska rzadko wystarczająca jest całkowicie dla
nowicjuszy sztuki kulinarnej — chociażby i z tego względu, że nie zawsze są pod ręką wszystkie
wymaganie składniki i kucharz skazany jest na improwizowanie, by doprowadzić szczytne
dzieło do szczęśliwego końca.
W tym rozdziale przedstawiamy różne metodologie; jest to swoisty przewodnik dla me-
nedżera dokonującego wyboru i przystosowującego przepis dla środowiska konkretnego pro-
jektu; dyskusja na temat wyboru właściwej metodologii wykraczałaby znacznie poza ramy książki.
Przedstawimy elementy przestrzeni projektowej, zarówno te „wagi lekkiej", jak i „wagi cięż-
kiej". Dzięki temu menedżerowi będzie zapewne łatwiej rozumieć tę przestrzeń, gdy będzie
organizował kolejne projekty i kierował ich realizacją. I mimo iż żadna bodaj książka nie jest
w stanie zastąpić praktycznego doświadczenia, przedstawimy kilka przykładów rzeczywistych
projektów w celu zilustrowania kompromisów, do jakich przychodzi menedżerowi dążyć
w związku z definiowaniem, ustanawianiem i realizowaniem projektów programistycznych.
Zaprezentujemy ponadto kilka analiz przypadków rozmaitej skali: od czteromiesięczne-
go projektu, realizowanego przez dwójkę programistów, pozostających w ciągłym kontakcie
z klientem, do trwającego rok projektu angażującego stu uczestników i realizowanego na
zamówienie zdalnego klienta. W każdej z tych analiz opiszemy wybór specyficznych metod
i technik, każda z nich przeznaczona jest na użytek czytelników mających wprawę w pro-
gramowaniu, lecz dopiero rozpoczynających poznawanie dyscypliny, jaką jest inżynieria
oprogramowania.
714 Rozdział 16, ® Wszystko razem, czyli metodologie

16.1. Wstęp: pierwsze zdobycie K21


Gdy M o u n t Everest (8850 m) został w 1953 roku zdobyty przy użyciu butli tlenowych — po
wielu bezskutecznych próbach bez nich — pojawiło się przekonanie, że na tak dużych wysokościach
tlen jest niezbędny. W efekcie nieodłącznym elementem włoskiej wyprawy na K2 (8611 m) w 1954
roku były właśnie butle tlenowe.

Nieprzewidziany obóz na K2
30 lipca 1954 Walter Bonatti i tragarz wysokogórski Mahdi wyruszyli z obozu 8. z niezbędnym
ładunkiem tlenu, przeznaczonego dla Achille Compagnoniego i Lina Lacedelliego, przebywających
obozie 9. i wytypowanych przez kierownika wyprawy jako kandydatów do zdobycia szczytu w pierw-
szej kolejności. Gdy Bonatti i Mahdi dotarli do ustalonej lokalizacji w obozie 9., nie spotkali tam
nikogo; po długotrwałych poszukiwaniach Compagnoniego i Lacedelliego udało im się w końcu
nawiązać z nimi jedynie kontakt głosowy. Około 21:30 zmuszeni zostali do powrotu do obozu.

Nieprzewidziany nocleg w obozie na wysokości 8100 m był istnym horrorem: Bonatti i Mahdi
musieli przetrwać nocną śnieżycę, bez jakiejkolwiek ochrony przed wiatrem. To niewiarygodne, ale
Bonatti wyszedł z tej gehenny bez większych obrażeń; niestety, Mahdi doznał szeregu odmrożeń, któ-
re ostatecznie doprowadziły do konieczności amputowania kilku palców u rąk i nóg. Następnego
dnia Bonatti i Mahdi zostawili swój bagaż w obozie i powrócili do obozu 8. Compagnoni i Lacedelli
zostawili swój namiot, zabrali pozostawiony dla nich tlen i ruszyli ku szczytowi. Według oficjalnej
kroniki wspinaczki, tlen skończył im się 200 m poniżej szczytu, który zdobyli o 18:00; jednak
niektóre zdjęcia przedstawiają leżące u ich stóp plecaki z butlami tlenowymi.

Dyskusja
Fakt, że Bonatti wyszedł bez szwanku z koszmarnego biwakowania, stał się przyczynkiem wielu oskar-
żeń, między innymi tego, że wbrew decyzji kierownika postanowił samodzielnie zdobyć szczyt.

Compagnoni i Lacedelli twierdzili, że tlen skończył się im przedwcześnie dlatego, że Bonatti zużył
jego część na biwaku, by zyskać siły do samotnej eskapady na szczyt; to jednak nie było możliwe,
gdyż Bonatti nie miał przy sobie maski tlenowej. Szczegółowa analiza fotografii w roku 1993 wyka-
zała, że Compagnoni i Lacedelli mieli założone maski tlenowe przez cały czas swej wyprawy na szczyt,
o czym pisze R. Marshall [Marshall, 2001]. Dlaczego więc kłamali?

Zdaniem Marshalla, Compagnoni musiał być przerażony, gdy po dotarciu do obozu 8. dowiedział
się, co wydarzyło się ubiegłej nocy. Czuł się częściowo odpowiedzialny za nieplanowany nocleg
Bonattiego i Mahdiego, obawiał się też posądzenia o przyczynienie się do obrażeń odniesionych
przez Mahdiego.

Mahdi powiedział oficerowi łącznikowemu — a też zdał sprawę kierownikowi wyprawy — że Bonatti
zamierza samodzielnie wyprawić się na szczyt. Mahdi nie mógł dowiedzieć się tego bezpośrednio
od Bonattiego, nie mieliby się bowiem jak porozumieć: Bonatti znał tylko język włoski, a Mahdi
mówił jedynie w U r d u — skąd więc taka konkluzja?

Jej źródeł należy szukać jeszcze przed drugą wojną światową. Wszystkie próby zdobywania
ośmiotysięczników wspomagane były wówczas przez doświadczonych Szerpów z Nepalu; Szer-
pów zatrudniano nawet na wyprawach na K2 i Nanga Parbat, położonych w Karakorum. Po

1
Przedstawiona dyskusja pomija cały szereg fascynujących szczegółów pierwszej wyprawy na K2. Aby
w pełni docenić złożone współzależności między wyposażeniem, motywacjami, strukturą organizacyjną,
ukrytymi motywami, oczekiwaniami, doświadczeniem i różnymi punktami widzenia, należałoby prze-
czytać rozdziały dotyczące K2 w książce Bonattiego [Bonatti, 2001], którą polecamy naszym czytelnikom.
16.1. Wstęp: pierwsze zdobycie K2 715

wojnie zatrudnienie lokalnych tragarzy stało się wręcz warunkiem koniecznym do uzyskania
zezwolenia na wspinaczkę. Oczywistymi kandydatami na pomocników byli Hunzowie z pół-
nocnego Pakistanu; byli ludźmi generalnie obytymi z górami, lecz nie mieli wówczas doświad-
czenia w wyprawach na takie wysokości.

W 1953 roku Hunza Mahdi brał udział, jako młodszy pomocnik, w pierwszym podejściu na
Nanga Parbat. Wyprawa rozpoczęła się od tradycyjnej „poręczówki", a jej uczestnicy zorgani-
zowani byli w hierarchiczną strukturę; m i m o to, ostatecznego wejścia na szczyt dokonał samo-
dzielnie H e r m a n n Buhl, po 40-godzinnej, heroicznej wspinaczce, czyniąc to wbrew postano-
wieniu kierownika wyprawy [Buhl, 1956].

Mahdi, pamiętny tej historii, nabrał widocznie przekonania, że tylko tak można skutecznie zdo-
bywać ośmiotysięczniki i na tej podstawie przypuszczał, że Bonatti podejmie samodzielną wspi-
naczkę, zamiast dostarczyć tlen kolegom. (Fałszywe) zarzuty Mahdiego pod adresem Bonattiego
musiały wywołać wściekłość Compagnoniego, który, widząc jawne pogwałcenie zarządzeń kie-
rownika, zrzucił na Bonattiego całkowitą odpowiedzialność za obrażenia odniesione przez Mahdiego.
Aby dodatkowo uwolnić się od współodpowiedzialności za te obrażenia, Compagnoni i Lacedelli
wymyślili historyjkę z przedwczesnym wyczerpaniem tlenu.

Kierownik ekspedycji wysokogórskiej musi podejmować szereg kluczowych decyzji, od


których uzależnione jest powodzenie (lub klęska) całego przedsięwzięcia: na którą górę się
wspinać, w jaki sposób zrealizować tę wspinaczkę, jakich użyć do tego narzędzi i kogo zaprosić
do udziału w wyprawie. Odpowiedź na te pytania prowadzi do różnych stylów wspinaczki: styl
„poręczowy" polega na zakładaniu coraz to wyższych obozów, połączonych systemem stałych
lin („poręczy") ułatwiających himalaistom łatwe przemieszczanie między obozami, natomiast
w stylu alpejskim w ogóle nie używa się stałych lin — uczestnicy wyprawy taszczą ze sobą całe
wyposażenie, zakładając obozy tylko na noclegi lub w sytuacji, gdy warunki pogodowe prze-
szkadzają w kontynuowaniu ekspedycji.
Po nieudanych próbach zdobycia Mount Everestu i K2 w latach 20. i 30. XX wieku, po-
wszechne stało się przekonanie, że szczyty te są nie do zdobycia bez dodatkowego dotlenienia
himalaistów. Gdy w 1953 roku zdobyto wreszcie Mount Everest z wykorzystaniem butli tle-
nowych, tlen postrzegany był jako element niezbędny do zdobywania w ogóle tak wysokich
szczytów. Podstawami tego przekonania zachwiali Reinhold Messner i Peter Habeler, zdoby-
wając Mount Everest bez pomocy butli tlenowych w 1978 roku.
Nie jest też oczywista odpowiedź na samo pytanie o sens wyłaniania kierownika wyprawy.
Gd początków lat 20., aż do późnych lat 60. XX wieku ekspedycje wysokogórskie organizowa-
ne były na sposób niemal wojskowy: narzucały hierarchiczne zależności między kierownikiem,
kilkoma wiodącymi wspinaczami, zespołem pomocników, licznymi tragarzami i oficerem
łącznikowym, którego zadaniem było łagodzenie problemów w porozumiewaniu się tragarzy
z pozostałymi uczestnikami. W ekspedycji na K2 w roku 1954 brało udział ponad trzystu traga-
rzy. W 1980 roku Reinhold Messner wszedł na Mount Everest przy pomocy jedynie dwóch ludzi,
którzy towarzyszyli mu tylko do obozu bazowego; ostatni odcinek na szczyt i z powrotem
pokonał samodzielnie, bez używania butli tlenowych.
Podobnie jak kierownik ekspedycji wysokogórskiej, menedżer projektu programistycz-
nego musi podejmować kluczowe decyzje dotyczące celów projektu, jego cyklu życiowego,
jego organizacji, wykorzystywanych, narzędzi, metod oraz składu osobowego zespołów. Mimo
iż niektóre z tych decyzji podejmowane są przez innych uczestników lub wynikają wprost
716 Rozdział 16, ® Wszystko razem, czyli metodologie

ze struktury organizacyjnej, ich całokształt ma krytyczny wpływ na projekt oraz zdolność


menedżera do rozwiązywania nieprzewidzianych problemów. W rozdziale 14. „Zarządzanie
projektem" dyskutowaliśmy decyzje menedżera związane między innymi z czasem, terminami,
kosztami i organizacją projektu, w rozdziale 15. „Cykl życiowy oprogramowania" zajęliśmy
się natomiast decyzjami niezależnymi od konkretnego projektu, związanymi z modelami pro-
cesów, zależnościami między aktywnościami, dojrzałością procesu i jego usprawnieniami.
Właśnie ze względu na złożoność materiału podzieliliśmy jego omówienie na dwa rozdziały,
jednakże w rzeczywistym projekcie podziału takiego dokonać nie sposób i menedżer traktować
musi łącznie wszystkie opisane zagadnienia jako silnie zależne od kontekstu projektu i uzależ-
nione wzajemnie. W tym rozdziale przedyskutujemy kilka podejść do podejmowania wspo-
mnianych decyzji w ramach spójnego frameworku; by dyskusja była bardziej czytelna, omó-
wimy oddzielnie cele projektu, środowisko projektu, metody, narzędzia i metodologie inżynierii
oprogramowania (patrz tabela 16.1).

Tabela 16.1. Przykłady celów, środowisk, metod i metodologii — w himalaizmie i inżynierii opro-
gramowania

Wspinaczka wysokogórska Inżynieria oprogramowania


Cel M o u n t Everest Zrealizowanie systemu zarządzania
K2 sytuacjami kryzysowymi, zgodnie
z wymaganiami dotyczącymi jakości,
Nanga Parbat
terminów i budżetu

Środowisko Mróz, dobra widoczność Lokalny klient


Tragarze Początkujący programiści
Wymaganie zezwolenia na wspinaczkę Czas trwania projektu
Rozproszenie realizacji projektu
Plan reagowania w sytuacjach awaryjnych

Metoda Wspinaczka klasyczna Modelowanie przypadków użycia


Przypisanie i odpinanie liny Wielokrotne wykorzystywanie wzorców
Oficer łącznikowy projektowych
Organizacja projektu

Narzędzie Butle tlenowe Narzędzie CASE


Lina Narzędzie kontroli wersji

Metodologia Styl alpejski Programowanie ekstremalne


Poręczówka Metodologia Royce'a

Cele projektowe obejmują kryteria oceny sukcesów w projekcie. Dla jednego pro-
jektu może to być jedynie dostarczenie konkretnego systemu w ustalonym terminie, dla
innego — głębokie aspekty strategiczne, takie jak zapewnienie powtarzalności reguł bizne-
sowych czy obniżanie kosztów biznesu.
Na środowisko projektu składają się elementy obecne na jego początku. W przypadku
projektu programistycznego środowisko definiowane jest przez klienta i bieżący stan organizacji
projektu. Elementy środowiska są czynnikami ograniczającymi menedżera, należą do nich
16.2. Środowisko projektu 717

między innymi kwalifikacje uczestników, typ rozwiązywanego problemu i umiejscowienie


realizacji projektu.
Metody są przepisami, pozostającymi do dyspozycji menedżera, który przez odpowiedni
ich wybór prowadzi projekt do szczęśliwego końca, w określonym środowisku. Przykładami
metod opisywanych dotychczas w tej książce są zbieranie wymagań i wykorzystywanie wzorców
projektowych. Różne metody odpowiednie są dla różnych środowisk — ekspedycja używająca
butli tlenowych potrzebuje większej liczby tragarzy.
Narzędzia to urządzenia lub programy wspomagające określone aktywności. Do na-
rzędzi przydatnych himalaistom zaliczają się butle tlenowe, liny, kotwy, namioty i tym po-
dobne; narzędziami programistycznymi są między innymi narzędzia modelowania, kom-
pilatory, debuggery, narzędzia śledzenia zmian i narzędzia kontroli wersji. Programiści często
wykorzystują specyficzne narzędzia do automatyzacji działań wynikających z używanej przez
nich metody.
Metodologią inżynierii oprogramowania jest kolekcja metod i narzędzi służących do
wytworzenia systemu informatycznego i zarządzania tym systemem, z zamiarem osiągnięcia
określonych celów w określonym środowisku. Metodologia definiuje zasady stosowania okre-
ślonych metod i narzędzi, także w przypadkach występowania nieprzewidywalnych zdarzeń.
W sekcji 16.2 opisujemy typowe elementy środowiska, mające znaczący wpływ na projekt.
W sekcji 16.3 dyskutujemy o typowych problemach, do rozwiązywania których wykorzysty-
wane są metodologie uwzględniające dane środowisko i zestaw dostępnych metod. Sekcję 16.4
poświęcamy przedstawieniu dwóch konkretnych metodologii: programowaniu ekstremalnemu
(Extereme Programming) opisanemu przez K. Becka i C. Andresa [Beck i Andres, 2005] oraz
jednolitej metodologii W. Roycea [Royce, 1998]; zamierzamy w ten sposób zilustrować spek-
trum metodologii dostępnych na potrzeby zarządzania projektami programistycznymi. Sekcja
16.5 zawiera opis typowych problemów metodologicznych w kontekście trzech analiz przypadku.

16.2. Środowisko projektu


W projekcie informatycznym wiele czynników ma wpływ na wybór odpowiednich metod.
W tej sekcji przedyskutujemy elementy środowiska, mające znaczący wpływ na inicjowanie
projektu, takie jak doświadczenie uczestników, typ klienta, użytkownicy, klimat technologiczny,
rozproszenie uczestników i czas trwania projektu.
Doświadczenie uczestników. Doświadczenie klienta, menedżera projektu i programi-
stów może ograniczać albo otwierać drogę do wykorzystywania rozmaitych metod realizacji
projektu. Doświadczenie to obejmuje między innymi znajomość dziedziny aplikacyjnej, dzie-
dziny realizacyjnej oraz różnych technik programistycznych i praktyk menedżerskich.
Typ klienta. Typ klienta determinuje naturę i efektywność sprzężenia zwrotnego między
klientem a programistami w czasie zbierania wymagań, a także szybkość podejmowania decyzji,
w sytuacji gdy niezbędne okazują się zmiany. Podstawę określenia typu klienta stanowią dwa
czynniki; zdolność decyzyjna i znajomość dziedziny aplikacyjnej; jako że są to czynniki binarne,
wyróżniamy cztery typy klientów (patrz tabela 16.2):

• lokalny klient samodzielny (local king client) ma pełną swobodę podejmowania decy-
zji, których nie musi z nikim konsultować, nie musi nikomu raportować problemów,
dobrze zna dziedzinę aplikacyjną i może kontaktować się z programistami oraz me-
nedżerem projektu w efektywny, często bezpośredni sposób,
718 Rozdział 16, ® Wszystko razem, czyli metodologie

• klient pełnomocnik (proxy client) jest reprezentantem delegowanym przez rzeczywi-


stego klienta, którego współpraca z organizacją realizującą projekt jest utrudniona
lub niemożliwa z powodu braku czasu czy oddalenia geograficznego; posiada znajo-
mość dziedziny aplikacyjnej wystarczającą do współdziałania z programistami w celu
rozwiązywania problemów, nie ma jednak uprawnień do podejmowania znaczących
decyzji,
• niby-klient (pseudo-client) to członek organizacji realizującej projekt — zwykle pra-
cownik działu marketingu lub programista — działający w imieniu potencjalnego
klienta o nieustalonej tożsamości, jako że projekt adresowany jest „na półkę" w no-
wym segmencie rynku, nie zaś na zamówienie konkretnego kontrahenta; niby-klient
może podejmować wszelkie decyzje bez konsultowania ich z kimkolwiek i może
efektywnie komunikować się z programistami i menedżerem, z oczywistych jednak
względów dysponuje ograniczoną znajomością dziedziny aplikacyjnej,
• brak klienta (no client) — wiele projektów, na przykład projekty wizjonerskie two-
rzone z myślą o wykreowaniu nowego segmentu rynku, rozpoczyna się bez kontekstu
klienckiego; jednak w większości przypadków w organizacji desygnowany jest
niby-klient, by racje programistów mogły być konfrontowane z (potencjalnymi) ra-
cjami przyszłych użytkowników.

Tabela 16.2. Zdolność decyzyjna i znajomość dziedziny aplikacyjnej dla różnych typów klienta

Dobra znajomość Słaba znajomość


dziedziny aplikacyjnej dziedziny aplikacyjnej
Duża zdolność decyzyjna lokalny klient samodzielny niby-klient

Mała zdolność decyzyjna klient pełnomocnik brak klienta

Kontakt z użytkownikami. Klient, mimo dobrej znajomości dziedziny aplikacyjnej,


może prezentować inny punkt widzenia niż użytkownicy. Zainteresowany jest zwykle termino-
wym dostarczeniem systemu o jak najbardziej rozbudowanej funkcjonalności, po jak najniższych
kosztach; użytkownicy oczekują natomiast znajomego interfejsu lub takiego, którego obsługi
łatwo mogą się nauczyć. Gdy rozbieżność obu tych interesów staje się znacząca, decydujące dla
powodzenia projektu okazują się testy użyteczności wykonywane przez użytkowników.
Klimat technologiczny. Zależnie od wymagań wyrażanych przez klienta, projekt może
być ograniczony pod względem wykorzystywania określonych komponentów technicznych.
Projekt polegający na usprawnieniu przestarzałego systemu wykorzystuje technologię dojrzałą
i dobrze znaną, podczas gdy prototypy bazujące na nowinkach technologicznych posługują
się często wstępnymi wersjami komponentów i mało dojrzałą technologią.
Rozproszenie geograficzne. Projekt, którego wszyscy uczestnicy skupieni są w jednym
pokoju, jest relatywnie łatwy w zarządzaniu, jako że uczestnicy znają się osobiście, a większa
część czynności koordynacyjnych realizowana jest w sposób nieformalny. W wielu jednak
przypadkach taka organizacja okazuje się niewykonalna lub niewskazana, bo projekt może być
realizowany z udziałem kilku dużych organizacji wnoszących doń zróżnicowane kwalifikacje
bądź też przez konsorcjum podwykonawców działających w oddaleniu geograficznym. W jesz-
cze innych przypadkach część uczestników wykonuje swe zadania w lokalizacjach wskazanych
przez klienta. Podczas gdy rozproszenie takie zwiększa dostępność wykwalifikowanych uczest-
16.3. Zagadnienia metodologiczne 719

ników, to jednocześnie stawia przed menedżerem nowe wyzwania w postaci spowolnionej


komunikacji, mniej sprawnej współpracy zespołowej i ryzyka utraty części informacji.
W opisywanej na wstępie wyprawie na K2 mieliśmy właśnie do czynienia z istotnym rozpro-
szeniem uczestników, skutkującym utratą informacji: kierownik ekspedycji pozostawał
w obozie bazowym, wytypowani przodownicy znajdowali się w obozie 9., a pomocnicy do-
starczający ostatnie butle z tlenem wyruszali z obozu 8.
Czas trwania projektu a częstotliwość zmian. Związek tych dwóch czynników ma za-
sadniczy wpływ na wybór cyklu życiowego projektu. Krótkie projekty, w czasie trwania
których używane technologie zmieniają się nieznacznie lub nie zmieniają w ogóle, nie wyma-
gają rozbudowanego zarządzania konfiguracją czy racjonalizacją; dla odmiany, projekty
obliczone na wiele lat realizacji, w trakcie których następują znaczące zmiany w wymaga-
niach i znacząca rotacja personelu, zyskują bardzo wiele dzięki systematycznemu podej-
ściu do zarządzania konfiguracją i racjonalizacją.

16.3. Zagadnienia metodologiczne


Metodologie dostarczają ogólne zasady i strategie do wyboru odpowiednich metod, a jako
swoiste wysokopoziomowe przewodniki bywają użyteczne w rozwiązywaniu nowych sytuacji.
W tej sekcji omówimy kilka zagadnień, dla których metodologie okazują się takim właśnie
przewodnikiem. Zagadnienie te można sformułować w postaci następujących pytań:

• Jak wiele planowania wykonać na początku? (patrz sekcja 16.3.1)


• Jak duża część projektu powinna opierać się na wielokrotnym wykorzystaniu tych
samych rozwiązań? (patrz sekcja 16.3.2)
• Jak dalece system powinien być modelowany przed rozpoczęciem kodowania? (patrz
sekcja 16.3.3)
• Jak szczegółowo powinno się zdefiniować procesy cyklu życiowego dla projektu?
(patrz sekcja 16.3.4)
• W jakim stopniu praca programistów powinna być kontrolowana i monitorowana?
(patrz sekcja 16.3.5)
• Kiedy powinno się przedefmiować cele projektu? (patrz sekcja 16.3.6)

16.3.1. Ile planowania?


Plany zarządzania projektem mają duże znaczenie dla jego powodzenia. Planowanie ma jednak
swoje ograniczenia; T. Gladwin w swym artykule [Gladwin, 1964], cytowanym przez L. A.
Suchmana [Suchman, 2007]) prezentuje je na przykładzie porównania metod nawigacji uży-
wanych przez Polinezyjczyków i Europejczyków. Europejski nawigator rozpoczyna od sporzą-
dzenia planu kursu, a później cały jego wysiłek skoncentrowany jest na realizacji tego planu;
w nieprzewidzianych okolicznościach plan zostaje skorygowany i podejmowane są czynności,
które mają na celu kontynuację podróży do zaplanowanego celu. Ten typ planowania spraw-
dził się szczególnie dobrze w procesie seryjnej produkcji identycznych samochodów, opisanej
przez F. W. Taylora na początku XX wieku [Taylor, 1911], niezbyt jednak nadaje się dla syste-
mów wymagających kreatywnego rozwiązywania problemów i szybkiego reagowania na zmiany.
720 Rozdział 16, ® Wszystko razem, czyli metodologie

Polinezyjczycy rozpoczynali nie od planu, a od zdefiniowania celu. W dążeniu do tego


celu reagowali ad hoc na nieprzewidywane okoliczności, wykorzystując pełnię swych umiejęt-
ności w zakresie obserwowania wiatru, fal, przypływów i prądów morskich, fauny, gwiazd,
chmur i odgłosu wody morskiej uderzającej w łodzie — to wszystko w celu określenia kierunku
nawigacji lub rozpoznania bliskości lądu. Zapytani o cel podróży mogliby odpowiedzieć na-
tychmiast, nie potrafiliby jednak określić aktualnego kursu. Taki właśnie sposób zarządzania
projektami charakterystyczny jest dla „zwinnych" metodologii i może być z powodzeniem
wykorzystywany w projektach eksplorujących nowe technologie — projektach, których
celem jest łamanie obowiązujących paradygmatów.
W cytowanej już książce L. A. Suchmana [Suchman, 2007] opisywane jest ponadto inne
ograniczenie planowania:

Planując wyprawę przy użyciu kanu, można zatrzymać się na chwilę i deliberować „popły-
nę jak najbardziej w lewo, spróbuję przepłynąć między tymi dwiema skałami, po czym zro-
bię szybki zwrot w prawo, by opłynąć tę kolejnąjednakże tego typu plany mogą okazać
się niewiele warte w konkretnej sytuacji, jaka wydarzyć się może już za chwilę. Gdy bo-
wiem przychodzi do faktycznego zmagania się z prądami, być może trzeba porzucić wszelkie
plany i sterować kanu za pomocą wszelkich dostępnych umiejętności.

W ogólności plany zarządzania projektem są szczególnie użyteczne przy rozpoczynaniu


projektu i spisują się dobrze, gdy projekt nie doświadcza znaczących zmian. Nie przydają się
jednak menedżerowi na wiele, gdy musi reagować na nieprzewidziane sytuacje, dla których
nie sposób zawczasu przewidzieć stosownych aktywności i konsekwencji, dopóki nie zostanie
podjęta jakaś akcja korygująca. Zacytujmy ponownie Suchmana:

Często jedynym wyjściem jest zmaganie się z konkretną sytuacją, dla której znany jest pe-
wien zestaw możliwości rozwiązania; nie potrafimy wówczas określić, przynajmniej co do
szczegółów, jaki pożądany stan przyszły chcielibyśmy osiągnąć.

16.3.2. Ile powtarzalności?


Wielokrotne wykorzystywanie stylów architektonicznych, wzorców projektowych i kompo-
nentów „z półki" może znacząco zredukować wysiłek konieczny do wyprodukowania systemu
(patrz rozdziały 6., 7. i 8.). Wszystko to jednak przy założeniu, że poszukiwanie powtarzalnych
rozwiązań lub komponentów odbywać się będzie w sposób efektywny i niezawodny, a wyko-
rzystywane elementy będą dla uczestników zrozumiałe w stopniu pozwalającym na oszacowanie
ich wpływu na resztę systemu. Co więcej, o ile gotowe komponenty mogą znacząco skrócić czas
tworzenia systemu, nie można tego powiedzieć o testowaniu systemu ani tym bardziej o na-
prawianiu usterek. W przypadku komponentów komercyjnych istnieje dodatkowe ryzyko
w postaci zaprzestania wspierania określonej ich wersji przez producenta.
Generalnie w związku z wielokrotnym wykorzystywaniem gotowych rozwiązań mene-
dżer staje przed koniecznością rozstrzygania trojakiego rodzaju kompromisów. Oto one.

• Powtarzalna architektura. W tym przypadku uczestnicy projektu adaptują predefi-


niowany projekt systemu do swoich własnych potrzeb (patrz rozdział 6.). Ponieważ
wybór architektury nie wiąże się z powtarzalnym wykorzystywaniem gotowego
oprogramowania, ocena istniejącej architektury nie jest wcale trudniejsza od oceny
16.3. Zagadnienia metodologiczne 721

architektury tworzonej „od zera", jednakże wybór gotowej architektury przyczynia


się do oszczędności wysiłku i czasu, w porównaniu z samodzielnym jej tworzeniem.
Problem jednak w tym, by wybrana przez programistów architektura umożliwiła im
spełnienie wymagań pozafunkcyjnych stawianych systemowi.
• Wzorce projektowe. Podobnie jak w przypadku architektury, wielokrotne wykorzy-
stywanie wzorców projektowych nie wiąże się z wielokrotnym stosowaniem istnieją-
cego oprogramowania (patrz rozdział 8.). Ponieważ wzorce projektowe nie dostar-
czają gotowych rozwiązań, a jedynie definiują ich ramy, programiści przystosowują je
i łączą w trakcie projektowania obiektów. Korzystanie ze wzorców projektowych wy-
maga jednak umiaru — nie należy ich używać dla samego faktu używania, bo może się
to odbywać ze szkodą dla spełniania wymagań pozafunkcyjnych i celów projektowych.
• Wielokrotne wykorzystywanie frameworków i komponentów. Wykorzystywanie kon-
kretnego frameworku lub zbioru komponentów narzuca ograniczenie na projekt
systemu i może zmuszać programistów do wykorzystywania konkretnych wzorców
projektowych (patrz rozdział 8.). Co więcej, gotowe komponenty są zwykle „czarny-
mi skrzynkami" dostarczanymi przez producenta i niemodyfikowalnymi. Menedżer
projektu musi więc starannie ocenić ryzyko związane z ich wielokrotnym wykorzy-
stywaniem. Ocena pod tym kątem danego frameworku może być przydatna w przy-
szłych projektach, uwalniając w ten sposób programistów od ponownego poszu-
kiwania właściwego frameworku.

Środowisko projektowe stanowi czynnik determinujący wybór strategii wielokrotnego


wykorzystywania gotowych rozwiązań. Dla dobrze zdefiniowanej dziedziny aplikacyjnej i ela-
stycznych wymagań klienta podejście bazujące na komponentach może być najbardziej efek-
tywne ekonomicznie. Dla nowych systemów tworzenie wszystkich rozwiązań „od zera" może
być jednak jedynym możliwym podejściem.

16.3.3. Ile modelowania?


Modelowanie to tworzenie abstrakcji systemu, koncentrującej się tylko na jego wybranym
aspekcie i ignorującej pozostałe aspekty. Dzięki temu zmaganie się z ogólną złożonością spro-
wadzone zostaje do rozumienia prostszych koncepcji. W miarę jak postępuje tworzenie systemu,
modele są stopniowo doskonalone i obejmują coraz to więcej szczegółów; jeśli jednak będzie
się to odbywać w sposób niekontrolowany, złożoność poszczególnych modeli może dorównać
złożoności oryginalnego systemu. Dzięki modelowaniu luźna i domyślna wiedza na temat sys-
temu staje się wiedzą jawną i sformalizowaną, dzięki czemu może być współdzielona przez
uczestników bez ryzyka nieporozumień. Modele mogą wspierać trzy różne typy aktywności:

• Projektowanie. Modele dostarczają reprezentacji umożliwiającej formułowanie roz-


maitych wniosków, założeń, twierdzeń i tym podobnych dotyczących systemu. Mo-
dele mogą także stanowić podstawę do generowania szablonów kodu źródłowego,
jak ma to miejsce w przypadku narzędzi CASE.
• Komunikowanie. Modele tworzą uniwersalny słownik, umożliwiający programistom
efektywne dyskutowanie o szczegółach projektu, nawet jeśli nie posiadają one jeszcze
odpowiednika w postaci rzeczywistego systemu. Modele mogą wspierać szeroką
722 Rozdział 16, ® Wszystko razem, czyli metodologie

gamę aktywności komunikacyjnych, od nieformalnej wymiany szkiców na serwetkach


w trakcie obiadu, do formalnych inspekcji kodu w pokoju programistów.
• Archiwizowanie. Modele stanowią zwięzłą reprezentację wiedzy o projekcie i racjo-
nalizacji tego projektu, dostępną do wykorzystywania w przyszłości. W przypadku
systemów długożyciowych nowe generacje programistów mogą dzięki temu łatwo
zapoznawać się z decyzjami podejmowanymi w przeszłości.

Menedżer projektu może wykorzystywać modelowanie do wspierania wymienionych aktyw-


ności, co generuje dla niego określone wyzwania.

• Formalizowanie wiedzy może być kosztowne, wymaga bowiem określonego czasu


i wysiłku ze strony programistów, jak również konsensusu ze strony wszystkich
uczestników w kwestii spójnego rozumienia tych samych znaczeń.
• Modele wprowadzają redundancję. Jeśli do systemu wprowadzane są zmiany, aktuali-
zowane muszą być także modele tego systemu opisujące te jego aspekty, które stają
się (bezpośrednio lub pośrednio) podmiotem zmian. Nieaktualne modele tracą swą
wartość.
• Modele stają się skomplikowane. Gdy złożoność modelu staje się porównywalna ze
złożonością samego systemu, pożytek ze stosowania tego modelu staje się wątpliwy.

Menedżer projektu może zadecydować o sprowadzeniu do minimum aktywności dedy-


kowanych utrzymaniu spójności i poprawności modelu, i o skierowaniu większego wysiłku
programistów w stronę kodowania i testowania. Rozmiar takiego posunięcia zależny jest od
rodzaju środowiska projektowego. Projekt zlokalizowany w jednym miejscu, realizowany dla
lokalnego samodzielnego klienta, nie wymaga zbyt szczegółowej dokumentacji, bowiem sam
klient jest „chodzącą dokumentacją" i może na bieżąco rozstrzygać z programistami wszelkie
wątpliwości. W przypadku projektu rozproszonego rzeczy mają się zgoła odmiennie: specyfi-
kacja wymagań i projekt systemu powinny ujawniać jak najwięcej informacji o systemie,
bowiem oddaleni od siebie uczestnicy nie mają okazji komunikowania tej informacji sposób
nieformalny. Opracowanie szczegółowego modelu przypadków użycia, ilustrowanego przykła-
dowymi scenariuszami, umożliwia wszystkim uczestnikom osiągnięcie zbieżności do spójnej
koncepcji i dokumentowanie podejmowanych decyzji oraz zobowiązań (patrz rozdział 4.).
Podobnie projekt o długim czasie realizacji stanowić może duże wyzwanie dla pamięci uczest-
ników oraz doświadczać rotacji kadr, istotne jest więc w jego przypadku dokumentowanie po-
dejmowanych decyzji i ich uzasadnień w postaci procesów zarządzania konfiguracją (patrz
rozdział 13.) i racjonalizacją kryjącą się za wspomnianymi decyzjami (patrz rozdział 12.).
Decyzja o stopniu dokumentowania wiedzy może także zależeć od czynników sterowa-
nia: uczestnicy sami stawiają się poza nawiasem struktury komunikacyjnej, rezygnując z dzielenia
się swym know-how z innymi uczestnikami. W opisywanej historii z wyprawy na K2 kierownik
ekspedycji nie podzielił się z Bonattim informacją otrzymaną od Mahdiego, był bowiem
wściekły, że uczestnicy ignorują jego polecenia (tak mu się przynajmniej wydawało). W rezul-
tacie Bonatti przez wiele lat nie mógł zrozumieć, dlaczego koledzy z ekspedycji unikają jego
towarzystwa, uważając go za kłamcę, który sprzedałby własną babcię, by tylko dostać się na
szczyt.
16.3. Zagadnienia metodologiczne 723

16.3.4. Ile procesów cyklu życiowego?


Modelowanie cyklu życiowego oprogramowania charakteryzuje się tymi samymi korzyściami
i wyzwaniami, co modelowanie systemu. Z jednej strony, umożliwia uczestnikom lepsze rozu-
mienie procesów, poszukiwanie możliwości lepszego ich implementowania i w konsekwencji
dzielenie się tą wiedzą z resztą organizacji, z drugiej jednak strony, wiedza dotycząca procesów
cyklu życiowego jest trudna do jawnego formułowania, a utrzymywanie modelu w aktualnym
stanie wymaga dodatkowego wysiłku.
Zadaniem menedżera jest wówczas znalezienie złotego środka między zupełnym bra-
kiem modelowania cyklu życiowego a szczegółowym, na bieżąco aktualizowanym modelem;
konkretny wybór uwarunkowany jest przede wszystkim ilością zasobów niezbędnych do za-
rządzania wspomnianym modelem. Dobrze rozpoznana dziedzina aplikacyjna i (lub) dzie-
dzina realizacyjna umożliwia definiowanie powtarzalnych procesów, o czym piszą M. C. Paulk,
C. V. Weber i B. Curtis [Paulk i in., 1995], co zwiększa korzyści z modelowania procesów po-
przez współdzielenie ich między różne projekty. W warunkach rozproszonej organizacji
współdzielenie wiedzy na temat procesów umożliwia wysoki stopień standaryzacji i w efekcie
zmniejsza ryzyko nieporozumień komunikacyjnych. Gdy przykładowo menedżer dysponuje
dobrze zdefiniowanymi procesami zarządzania konfiguracją i kontrolowania zmian (patrz
rozdział 13), może nie tylko łatwo śledzić status poszczególnych zmian, lecz także przewidywać
częstotliwości tych zmian w przyszłości. Dla odmiany, szybkie realizowanie kolejnych prototy-
pów i eksplorowanie koncepcji skutkuje procesem znacznie bardziej chaotycznym, przypomi-
nającym burzę mózgów; ze względu na krótkotrwałość tego procesu i jego niepowtarzalność
wysiłek włożony w zarządzanie procesami okazałby się nieopłacalny; w takich sytuacjach
lepsze okazują się modele cyklu życiowego zorientowane na encje lub zupełny brak cyklu ży-
ciowego explicite.
Upowszechniając modele cyklu życiowego między projektami, menedżer powinien
upewnić się, że jego generalizacja oparta jest na rzetelnej wiedzy. W opisywanej wyprawie na
K2 Mahdi, pamiętając ekspedycję na Nanga Parbat, która rozpoczęła się od hierarchicznej
„poręczowej" organizacji, a zakończyła samotną wspinaczką Buhla ignorującego polecenia
kierownika, na tej podstawie wyrobił sobie przekonanie, że tak i tylko tak (w stylu alpejskim)
kończyć się mogą wszelkie wyprawy na tak wysokie szczyty.

16.3.5. Ile kontroli i monitorowania?


Szczegółowy model cyklu życiowego prowadzić może do szczegółowego planu, który podlegać
będzie konfrontowaniu z rzeczywistym postępem realizacji projektu. Szczegółowość tego
planu może zmieniać się od codziennego monitorowania postępu każdego z uczestników do
wyznaczenia pojedynczego „kamienia milowego" oznaczającego dostarczenie produktów
finalnych i pozostawienie uczestnikom zaplanowanie swej własnej pracy. Jako że obie te skraj-
ności są z gruntu nierozsądne, zadaniem menedżera jest znalezienie rozwiązania pośredniego,
odpowiedniego dla środowiska konkretnego projektu.
W ogólności zarówno planowanie, jak i ocenianie bieżącego postępu wymaga pewnego
doświadczenia — nowicjusze zwykle są kiepskimi sędziami własnych osiągów, podobnie brak
precedensów utrudnia planowanie nowatorskich systemów. By pomóc nowicjuszom, menedżer
może ustanowić hierarchiczną organizację raportowania, pośrodku której plasować się będą
724 Rozdział 16, ® Wszystko razem, czyli metodologie

bardziej doświadczeni programiści. Może ona także zdefiniować „kamienie milowe" jako
prezentacje i przeglądy oparte na scenariuszach (nie na dokumentach), co skłaniać może
uczestników do wczesnego konfrontowania się z trudnymi zagadnieniami. W celu lepszej
kontroli nowatorskich projektów menedżer może zwiększyć częstotliwość „kamieni milo-
wych", pozostawiając ich „zawartość" odpowiednio elastyczną dla poszukiwania dodatkowych
okazji do doskonalenia i rewidowania celów projektowych. Menedżer powinien także wyko-
rzystywać wiedzę doświadczonych programistów, umożliwiając im rewidowanie planu pro-
jektu lub modelu procesów. W przypadku projektu realizowanego przez programistów zarówno
doświadczonych, jak i początkujących menedżer powinien tak organizować przeglądy part-
nerskie, wędrówki po kodzie i programowanie parami, by ułatwić transfer wiedzy od ekspertów
do nowicjuszy.
W czasie wyprawy na K2 hierarchiczna organizacja okazała się źródłem wąskich kanałów
komunikacyjnych: Mahdi opowiedział o swym przypuszczeniu oficerowi łącznikowego, ten
przekazał informację kierownikowi, kierownik nie porozmawiał jednak z Bonattim w celu jej
wyjaśnienia.

16.3.6. Kiedy przedefiniować cele projektu?


Niekiedy okazuje się, że cel projektowy jest nie do zrealizowania, bo zbyt ambitny bądź po
prostu źle sformułowany. Wówczas najlepszym wyjściem wydaje się pogodzenie z porażką
oraz dokładne przeanalizowanie i rozpoznanie jej przyczyn. Wymaga to od menedżera nie
lada umiejętności psychologicznych, w końcu od początku realizacji projektu jego zadaniem
było przekonywanie uczestników, że projekt jest realizowalny. Jednym ze sposobów pogo-
dzenia tej sprzeczności jest uznanie porażki za planowany wynik projektu i wyciągnięcie po-
uczających wniosków — w końcu projekt realizowany bez większych problemów nie jest
w stanie dostarczyć wielu takich pouczających wniosków. Nie ma tego złego, co nie wyszłoby
na dobre — porażka jednego projektu pomaga zidentyfikować i usunąć przeszkody stwarzające
ryzyko podobnych porażek w przyszłych projektach.
Chociaż takie „szczęśliwe nieszczęście" jest pewnym sposobem na „skasowanie" pro-
jektu i wyciągnięcie wniosków z własnych i cudzych błędów, w praktyce rzadko można sobie
na nie pozwolić. Alternatywą jest więc użycie końcowego statusu projektu jako przedefinio-
wanego celu projektowego, co pozwoli na uratowanie owoców dotychczas wykonanej pracy
w postaci zrealizowania celu nie tak bardzo ambitnego jak pierwotny, ale jednak rozsądnego
i użytecznego.

16.4. Spektrum metodologii


Metodologia inżynierii oprogramowania to spójny zbiór zasad postępowania z zagadnie-
niami opisanymi w poprzedniej sekcji. W himalaizmie styl „poręczówki" i styl alpejski to wła-
śnie przykłady metodologii. Gdy tylko kierownik ekspedycji zdecyduje się na wybór okre-
ślonego stylu, następuje określenie metod postępowania. Jako że przykładowo na dużych
wysokościach tlen atmosferyczny nie jest dla człowieka wystarczający na dłuższą metę, a styl
poręczowy wiąże się z budowaniem coraz wyższych obozów, wyposażonych w namioty, śpi-
wory, pojemniki żywnością, zbiorniki z paliwem, przeto uczestnicy muszą wykorzystywać do-
datkowe zapasy tlenu i znakomita większość „poręczowych" ekspedycji na tak duże wysokości
16.4. Spektrum metodologii 725

wykorzystuje butle tlenowe jako podstawowy element ekwipunku. Dla odmiany, styl alpejski
— z tego samego powodu — zakłada wyprawę na szczyt dostatecznie szybką, by wystarczający
dla wspinacza okazał się tlen atmosferyczny.
Na tej samej zasadzie odmienne style tworzenia oprogramowania stanowią różne kom-
binacje organizacji projektu i jego cykli życiowych. Tradycyjnie, metodologie inżynierii opro-
gramowania rozpoczęły swą ewolucję od dużych, złożonych projektów, charakteryzujących się
rozległą fazą planistyczną, szczegółowym modelowaniem (czyli rozbudowaną dokumentacją),
hierarchiczną organizacją i drobnoziarnistym planowaniem. Tak duży narzut organizacyjny
okazywał się nieuzasadniony dla krótkich i rutynowych projektów, pojawiła się więc alternatywa
w postaci „zwinnych" (agile) metodologii, takich jak programowanie ekstremalne (Extreme
Programming — opisane przez K. Becka i C. Andresa [Beck i Andres, 2005]), projektowanie
sterowane cechami (Feature-Driven Design, o którym piszą S. Palmer i J. Felsing [Palmer i Fel-
sing, 2002]) czy też opisywana w rozdziale 14. metodologia Serum (omówiona przez K. Schwa-
bera i M. Beedle'a[Schwaber i Beedle, 2002]). „Zwinność" tych metodologii zasadza się na
przyrostowym planowaniu, kod źródłowy zyskuje status najważniejszego modelu, a luźno
powiązane zespoły wykwalifikowanych programistów mają swobodę planowania i organizo-
wania swej pracy. Dodatkową zaletą zwinnych metodologii jest ich duża zdolność adaptacji do
fundamentalnych nawet zmian w wymaganiach, omówiona w pracy A. Cockburna [Cock-
burn, 2001]. Zgodnie z ogólnie przyjętą definicją, przedmiotowa „zwinność" to „zdolność za-
równo do tworzenia zmian, jak i sprawnego na nie reagowania, w obliczu burzliwego środo-
wiska biznesu". Cytat pochodzi z publikacji J. Highsmitha [Highsmith, 2004].
Tak samo jak w himalaizmie, środowisko ogranicza zbiór metod możliwych do wyboru:
styl alpejski może być sposobem na szybkie zdobycie szczytu przez sprawnego wspinacza, ale
jest raczej nieprzydatny jako metoda zaprowadzenia na Mount Everest amatorów traktujących
wspinaczkę dla rozrywki (za niezłą odpłatnością, rzec jasna). Podobnie zwinne metodologie
inżynierii oprogramowania mogą skutkować spektakularnymi wynikami, ale tylko w przypad-
ku dostępności „masy krytycznej" wysoce wykwalifikowanych uczestników projektu.
W sekcji 16.4.1 opiszemy metodologię Royce'a — patrz książka W. Royce'a [Royce, 1998]
— stanowiącą programistyczną analogię stylu poręczowego wspinaczki. Metodologia ta wynika
wprost z założeń jednolitego procesu {Unified Process) opisywanego w rozdziale 15. i dostarcza
menedżerowi wiele heurystyk pomocnych w szacowaniu, kontrolowaniu i monitorowaniu
projektów. Sekcję 16.4.2 poświęcimy programowaniu ekstremalnemu (XP — Extreme Pro-
gramming) [Beck i Andres, 2005] — wczesnej „zwinnej" metodologii stanowiącej analogię
stylu alpejskiego. XP minimalizuje generowanie modeli i dokumentacji, skupiając się na uwi-
dacznianiu projektu systemu wprost w kodzie źródłowym i rozpowszechnianiu związanej z nim
wiedzy w sposób bezpośredni. W sekcji 16.4.3 opiszemy ewoluowanie zwinnych technologii,
od metodologii rugby, opisanej przez H. Takeuchiego i I. Nonakę [Takeuchi i Nonaka, 1986],
którą można porównać do wspinaczki na nieznany szczyt, do nowoczesnych podejść, na czele
z metodologią Serum [Schwaber i Beedle, 2002].

16.4.1. Metodologia Royce'a


Metodologia Royce'a opiera się na jednolitym procesie (Unified Process) jako modelu cyklu
życiowego, opisanym w rozdziale 15. Jest metodologią iteratywną, skoncentrowaną na zarzą-
dzaniu ryzykiem oraz zarządzaniu zmianami i różni się zasadniczo od podejść „kaskadowych"
typowych dla dużych projektów. Kluczowymi jej cechami są:
726 Rozdział 16, ® Wszystko razem, czyli metodologie

• Pierwszeństwo architektury, czyli skupienie uwagi na krytycznych przypadkach użycia,


znaczących decyzjach architektonicznych i planach cyklu życiowego przed zaanga-
żowaniem zasobów w realizację w pełnej skali.
• Iteratywny cykl życiowy i wczesne wykrywanie ryzyka, czyli szczególne traktowanie
krytycznych przypadków użycia, konstrukcji architektonicznych wysokiego ryzyka
i jak najwcześniejsze identyfikowanie obszarów niepewności. Każda iteracja powinna
skupiać się na specyficznym czynniku ryzyka i traktowaniu w jego kontekście wyma-
gań, architektury oraz planowania.
• Programowanie oparte na komponentach, czyli minimalizowanie objętości kodu two-
rzonego bezpośrednio przez programistów przez wykorzystywanie gotowych kompo-
nentów i generatorów kodu oraz posługiwanie się językami wysokiego poziomu.
• Środowisko zarządzania zmianami, czyli automatyzacja wprowadzania i rejestrowania
zmian wynikających z iteracyjnego charakteru procesów cyklu życiowego.
• Inżynieria wahadłowa (round-trip engineering), czyli automatyzacja powiązań modeli
z kodem źródłowym, redukująca koszt wprowadzania zmian.
• Obiektywna kontrola jakości, czyli automatyczne zbieranie wartości metryk i wskaź-
ników jakości, umożliwiających ocenę postępu w miarę, jak architektura produktu
zmierza w stronę ostatecznego kształtu.
• Używanie wizualnych języków modelowania, takich jak UML, w celu łatwiejszego
konstruowania, prezentowania i dokumentowania modeli.
• Podejście oparte na prezentacjach, umożliwiające wczesne wykrywanie problemów
z wydajnością i ocenę pośrednich artefaktów.

Zobaczmy teraz, jak założenia te odzwierciedlane są w obszarach planowania, modelo-


wania, powtarzalnego używania rozwiązań, procesów cyklu życiowego i kontrolowania projektu.

Ile planowania?

Zgodnie z metodologią Roycea plan projektu opracowywany jest iteracyjnie, podobnie


jak samo oprogramowanie. Plany są doskonalone i uszczegółowiane, w miarę jak uczestnicy
powiększają swą wiedzę na temat dziedziny aplikacyjnej i dziedziny realizacyjnej. Z błędami
planowania rzecz ma się tak samo jak z błędami programistycznymi; im wcześniej zostaną
rozwiązane, tym mniejszy będzie ich wpływ na powodzenie projektu.
Metodologia Royce'a organizuje podział pracy wokół aktywności cyklu życiowego.
Elementy na pierwszym poziomie struktury podziału pracy reprezentują przepływy pracy
w cyklu życiowym — zarządzanie, przetwarzanie wymagań, projektowanie, implementowanie,
szacowanie i wdrażanie. Na drugim poziomie plasują się elementy reprezentujące poszczególne
fazy — Inception, Elaboration, Construction i Transition. Na trzecim, najniższym poziomie
reprezentowane są artefakty produkowane w poszczególnych fazach. W przypadku szczególnie
złożonych projektów w strukturze WBS mogą pojawić się dodatkowe poziomy związane z gru-
powaniem wspomnianych artefaktów.
Zorganizowanie podziału pracy wokół cyklu życiowego, a nie wokół samego systemu,
sprawi, że podział ten staje się bardziej niezależny od zmian wprowadzanych do systemu
16.4. Spektrum metodologii 727

Jako że czynienie wszelkich oszacowań w ramach owego projektu jest dosyć trudne,
Royce sugeruje wyliczenie początkowych oszacowań przy użyciu odpowiedniego modelu, takiego
jak COGOMO II (patrz praca B. Boehma, E. Horowitza, R. Madachy'ego, D. Reifera, B. K.
Clarka, B. Steecea, A. W. Browna, S. Chulaniego i C. Abtsa [Boehm i in., 2000], i następnie
ich urealnienie przy udziale menedżera projektu, programistów i testerów. Umożliwi to uwzględ-
nienie różnych punktów widzenia we wspomnianych oszacowaniach i wzmocni w uczestnikach
poczucie współodpowiedzialności za ich realność. Po każdej iteracji plan projektu jest przeglą-
dany i rewidowany w celu odzwierciedlenia rzeczywistej wydajności projektu i naprawienia
ewentualnych błędów.

Ile powtarzalności?

Jedną z kluczowych zasad metodologii Royce'a jest minimalizowanie kodu tworzonego


bezpośrednio przez programistów. Zasadę tę realizuje się poprzez wykorzystywanie gotowych
komponentów komercyjnych, automatyczne generowanie kodu przez rozmaite narzędzia oraz
posługiwanie się wizualnymi środowiskami programistycznymi. Wykorzystywanie gotowych
rozwiązań Royce traktuje jako inwestycję, która powinna owocować zmniejszeniem czasu two-
rzenia systemu. Dojrzałe komponenty i narzędzia mogą także przyczynić się do zredukowania
czasu usuwania usterek; narzędzia i komponenty wątpliwej jakości mogą powodować problemy
jakościowe w stopniu niweczącym wszelkie korzyści wynikające z oszczędności kodowania.
Wybór między wykorzystywaniem gotowych komponentów a tworzeniem własnych jest więc
obarczony ryzykiem, które powinno zostać ocenione jak najwcześniej w cyklu życiowym (na
przykład w 1. iteracji fazy Elaboration). Gdy gotowe komponenty wykorzystywane są z powo-
dzeniem w kilku projektach, zysk ze wspomnianej inwestycji staje się jeszcze większy.

Ile modelowania?

Royce klasyfikuje artefakty w oparciu o aktywności jednolitego procesu (patrz sekcja


15.4.2).

• Artefakty menedżerskie związane są z aktywnościami planowania i monitorowania.


Mają formę notacji ad hoc, przedstawiającej „kontrakty" między uczestnikami
projektu a innymi udziałowcami. Do artefaktów tej grupy należą między innymi
deklaracja problemu, plan zarządzania projektem, plan zarządzania konfiguracją
i opis statusu.
• Artefakty związane z przetwarzaniem wymagań opisują między innymi wizjonerskie
scenariusze, prototypy interfejsów użytkownika i model analityczny.
• Artefakty projektowe związane są z architekturą oprogramowania i specyfikacją
interfejsów.
• Artefakty implementacyjne to przede wszystkim kod źródłowy, komponenty i pliki
wykonywalne niezbędne do testowania składników systemu.
• Artefakty wdrożeniowe obejmują wszystkie produkty finalne wynegocjowane między
klientem a menedżerem projektu, między innymi kod wykonywalny, podręcznik
użytkownika i podręcznik administratora.
728 Rozdział 16, ® Wszystko razem, czyli metodologie

Artefakty testowe, wynikające z oceniania poszczególnych produktów, rozproszone są


między wymienionymi grupami: plany testów i definicje procedur testowania to artefakty
menedżerskie, specyfikacje testów to artefakty związane z wymaganiami. Testy traktowane
są zwykle jako część systemu, tworzone są bowiem przy użyciu tych samych narzędzi, za
pomocą których tworzy się pozostałe jego komponenty.
Diagram wszystkich artefaktów generowanych w poszczególnych fazach nazywany jest
często mapą artefaktów (artifact road map). Na rysunku 16.1 widoczny jest przykład takiej
mapy dla dużego projektu zarządzanego zgodnie z metodologią Royce'a.

Rysunek 16.1. Przykład mapy produktów w projekcie wykorzystującym jednolity proces Royce'a jako
model cyklu życiowego. Nieformalne emisje produktów oznaczone są białymi trójkątami, zaś produkty
posiadające status linii bazowej reprezentowane są przez wypełnione trójkąty. Linie kropkowane repre-
zentują końce poszczególnych faz (rysunek na podstawie pracy W. Royce'a [Royce, 1998])

Ile procesów cyklu życiowego?

Na użytek wyboru odpowiedniego cyklu życiowego metodologia Royce'a dostarcza heu-


rystyki i wskazówki bazujące na technicznej i menedżerskiej złożoności projektu, zdetermino-
wanej przez następujące czynniki. Oto one.

• Skala projektu. Liczba uczestników projektu jest najważniejszym czynnikiem warun-


kującym wybór procesów cyklu życiowego. Małe projekty, realizowane przez maksi-
mum pięciu uczestników wymagają mniejszego narzutu menedżerskiego niż projekty
angażujące kilkudziesięciu lub kilkuset uczestników. Sprawność realizacji małych
projektów jest silnie uzależniona od technicznych umiejętności poszczególnych
uczestników i od narzędzi, jakie mają do dyspozycji. W większych projektach istot-
niejsze są umiejętności menedżerskie kierowników zespołów — gdy niedostateczne,
16.4. Spektrum metodologii 729

stają się pierwotną przyczyną wąskich gardeł wydajności. Małe projekty charaktery-
zują się nieformalnym zarządzaniem koncentrującym się na technicznych artefaktach,
nielicznymi „kamieniami milowymi" i brakiem formalnych procesów. Większe pro-
jekty muszą być zarządzane w sposób bardziej formalny, ściśle definiować „kamienie
milowe" i koncentrować się na artefaktach zarządzania zmianami, między innymi
na żądaniach zmian i uwzględnieniu ich w zarządzaniu konfiguracją.
• Spójność udziałowców. Grupa współpracujących udziałowców posługuje się elastycz-
nym planem i nieformalnymi porozumieniami; przykładem projektu tego rodzaju
może być wytwarzanie produktu „na półkę" przez początkującą na rynku kilkuoso-
bową firmę. Zgodność i współdziałanie umożliwia doskonalenie planu i wymagań
po każdej iteracji. Dla odmiany, różnice zdań i konflikty między udziałowcami stwa-
rzają konieczność formalizowania porozumień i dobrze zdefiniowanych procesów,
by możliwe było osiągniecie konsensusu. Konflikty między udziałowcami zdarzają się
często w przypadku projektów realizowanych przez konsorcja firm, prezentujących
sprzeczne cele projektowe. W konfliktowej grupie udziałowców większy nacisk kładzie
się więc na modelowanie przypadków użycia i przeglądy bazujące na prezentacjach.
Krytyczny staje się proces weryfikowania i walidacji, a aktywności związane z prze-
glądami i zapewnieniem jakości mają pierwszeństwo przed innymi.
• Elastyczność procesów. Rygory kontraktu między klientem a firmą realizującą projekt
przekładają się na rygoryzm definiowania procesów cyklu życiowego. Rygorystyczny
kontrakt, na przykład zamówienie ze strony administracji publicznej, wymaga zwykle
precyzyjnego zdefiniowania procesów, gdy na przykład tworzenie eksperymentalnych
prototypów na własne potrzeby cechuje się znacznie większą swobodą.
• Dojrzałość procesów. Organizacje projektów realizowanych przy użyciu dojrzałych
procesów są łatwiejsze w zarządzaniu niż organizacje polegające na procesach niedoj-
rzałych. Dojrzałe organizacje prezentują doświadczenie umożliwiające precyzyjne
planowanie i wysoki stopień automatyzacji procesów.
• Ryzyko architektoniczne. W idealnym przypadku opłacalność wybranej architektury
powinna być zaprezentowana przed pełnym jej zastosowaniem. W praktyce jednak
nie zawsze możliwe jest wyeliminowanie ryzyka związanego z architekturą przed
rozpoczęciem fazy konstrukcyjnej. Nowy i nieprzetestowany styl architektoniczny
niesie ze sobą większe ryzyko w postaci kosztownych przeróbek kodu; wczesne roz-
poznanie tego ryzyka wymaga większego wysiłku projektowego i większego nacisku
na prezentacje.
• Doświadczenie dziedzinowe. Projekt realizowany przez uczestników mających wiedzę
i doświadczenie z zakresu dziedziny aplikacyjnej i dziedziny realizacyjnej cechuje się
krótszymi fazami początkowymi cyklu życiowego, głównie dzięki wykorzystywaniu
procesów opracowanych na potrzeby poprzednich projektów. Programiści mający
doświadczenie w opracowywaniu podobnych systemów w przeszłości stosują zwykle
mniej iteracji i zadowalają się mniejszą liczbą ocen statusu.

Podczas inicjowania projektu menedżer powinien wziąć pod uwagę wymienione czynniki,
decydując o stopniu szczegółowości procesów oraz o przydziale budżetu dla każdej z faz.
730 Rozdział 16, ® Wszystko razem, czyli metodologie

Ile kontroli?

Trudno zarządzać projektem, którego statusu nie da się mierzyć w sposób obiektywny.
To jeden z powodów tego, że trudno zarządzać projektem tradycyjnym, ponieważ większość
jego artefaktów stanowią dokumenty. Iteracyjny cykl życiowy częściowo rozwiązuje ten pro-
blem, gdyż szybko pojawia się w nim, obok dokumentów, wiele artefaktów o charakterze tech-
nicznym. Celem zbierania metryk jest bieżące dostarczanie adekwatnego obrazu postępu
w realizacji projektu i stanu jakościowego ewoluujących produktów, tak by można było cały
czas korygować budżet i harmonogram.
Na początku każdej iteracji aktualizowane są plan, wymagania i architektura systemu.
Ponadto rezultatem każdej iteracji są między innymi zmiany wymagań dotyczących produktów
objętych kontrolą konfiguracji, w celu sprostania nowo rozpoznanym czynnikom ryzyka.
W iteracyjnym procesie cyklu życiowego krytycznego znaczenia nabiera więc zarządzanie
zmianami: każda zmiana powinna być dokładnie śledzona, od jej zażądania, po aprobatę,
przeznaczenie do implementacji i samo implementowanie. Rozmiar kodu modyfikowanego,
dodawanego lub usuwanego w związku ze zmianą, rozmiar samej zmiany i inne podobne
metryki mają krytyczne znaczenie z perspektywy kontrolowania ogólnej jakości systemu.
Metodologia Royce'a skupia się na trzech metrykach menedżerskich i czterech metrykach
jakościowych. Do pierwszej z tych grup należą:

• praca — ile zadań zostało dotychczas zakończonych i jak ma się ta liczba do planu
zadań?
• koszty — ile zasobów zostało już skonsumowanych i jak ma się to do zaplanowanego
budżetu?
• dynamika kadrowa — ilu uczestników zrezygnowało z udziału w projekcie? Ilu
przybyło nowych uczestników?

Metryki jakościowe obejmują natomiast:

• dynamikę zmian— ile żądań zmian formułowanych jest w określonym przedziale


czasu?
• przełamywanie kodu— jak wiele kodu źródłowego należy dodać, zmienić lub usunąć
w związku z implementacją danej zmiany?
• przeróbkę — jak wiele wysiłku wymaga zaimplementowanie danej zmiany?
• średni czas między awariami — jak wiele usterek wykrywanych jest w ciągu jednej
godziny testowania?

Gdy zbieranie wymienionych metryk zostanie zautomatyzowane — lub w inny sposób


zminimalizowana zostanie ingerencja człowieka konieczna do ich zbierania — menedżer
projektu może polegać na nich, bo dają coraz lepszy obraz statusu i jakości systemu. Przykła-
dowo częstotliwość żądania zmian można łatwo obliczać, dysponując narzędziem śledzenia
zmian, stopień przełamywania kodu łatwo wyliczać na podstawie komparatorów, w jakie wy-
posażone są narzędzia kontroli wersji, zaś średni czas między awariami można łatwo obliczyć
na podstawie danych dostępnych dzięki narzędziom śledzenia usterek.
16.4. Spektrum metodologii 731

Podsumowanie

W tabeli 16.3 znajduje się podsumowanie metodologii Royce'a w podziale na każde z opi-
sanych zagadnień.

Tabela 16.3. Metodologia Royce'a w podziale na kategorie zagadnień

Zagadnienie Metody
Planowanie Ewolucyjna struktura podziału pracy
Wstępne oszacowanie kosztu i h a r m o n o g r a m u , oparte na modelu (na
przykład C O C O M O II)
Iteracyjne planowanie, z udziałem wszystkich zainteresowanych

Modelowanie Pierwszeństwo dla krytycznych przypadków użycia i kluczowych wymagań


Pierwszeństwo dla architektury systemu
Wykorzystywanie języka UML
Wykorzystywanie inżynierii wahadłowej

Wielokrotne Decyzje wyboru między tworzeniem z zakupem k o m p o n e n t ó w


wykorzystywanie Minimalizowanie kodu tworzonego bezpośrednio przez programistów,
gotowych rozwiązań dzięki wykorzystywaniu gotowych k o m p o n e n t ó w i generatorów kodu

Procesy Skala projektu


Spójność udziałowców
Elastyczność procesów
Dojrzałość procesów
Ryzyko architektoniczne
Doświadczenie w dziedzinie aplikacyjnej i dziedzinie realizacyjnej

Kontrola Metryki menedżerskie (praca, koszty, dynamika kadrowa)


Metryki jakościowe (dynamika zmian, przełamywanie kodu, przeróbka,
średni czas między awariami)

16.4.2. Programowanie ekstremalne (XP)


Programowanie ekstremalne (XP — Extreme Programming) to metodologia adresowana do
małych zespołów, zamierzających szybko tworzyć oprogramowanie w zmieniającym się śro-
dowisku. Istotą tej metodologii jest jak największe uwidocznienie projektu systemu w jego ko-
dzie źródłowym, co znacznie redukuje zarówno zapotrzebowanie na dokumentację, jak i ryzyko
powstania niespójności między poszczególnymi dokumentami. Projekt systemu tworzony jest
przyrostowo — implementowane są wyłącznie cechy związane z aktualnie rozpatrywanym
wymaganiem. Zamiast projektowania a priori obszernej architektury, zdolnej sprostać przy-
szłym zmianom, programiści ograniczają się w danym czasie do jednego wymagania: naj-
pierw istniejący kod poddawany jest refaktoryzacji w celu usprawnienia projektu systemu
pod kątem bieżącego wymagania, następnie poprawiony kod jest testowany; po zaliczeniu
wszystkich testów tworzone są przypadki testowe dla nowego wymagania, w końcu imple-
mentowane jest samo wymaganie. Projekt systemu jest więc udoskonalany pod względem
rozszerzalności dopiero wtedy, gdy pojawia się potrzeba jego rozszerzenia.
732 Rozdział 16, ® Wszystko razem, czyli metodologie

XP dostarcza wiele heurystyk pomocnych w zarządzaniu projektem. Przykładowo całość


kodu źródłowego jest własnością zespołu programistów, nie ma przyporządkowania konkret-
nych fragmentów tego kodu poszczególnym programistom. Wspólna własność kodu, upraw-
niająca każdego programistę do dokonywania niezbędnych zmian, bliska jest schematowi pro-
gramowania bezosobowego (egoless programming) zaproponowanego przez G. M. Weinberga
[Weinberg, 1971].
Drugą, oprócz wspólności kodu, cechą XP jest programowanie parami. Każdy wiersz ko-
du tworzony jest przez parę programistów, dynamicznie przydzielanych do konkretnego
zadania; jeden z programistów, zwany prowadzącym (driver), kontroluje klawiaturę i mysz,
drugi — zwany partnerem — patrzy mu na ręce (i na ekran).
Całość metodologii XP można scharakteryzować w postaci następujących pięciu kluczo-
wych zasad.

• Szybkie sprzężenie zwrotne. Szybkie wykrywanie problemów daje więcej czasu na ich
rozwiązywanie; odnosi się to zarówno do relacji klienta z programistami, jak i do
testowania systemu.
• Prostota. Projekt powinien koncentrować się tylko na bieżącym wymaganiu. Jako że
żądania zmian mogą pojawiać się dość często i tylko niektóre z nich można przewi-
dywać, łatwiej zajmować się nowymi wymaganiami dopiero wtedy, gdy się rzeczywi-
ście pojawią. Prosty projekt jest łatwiejszy do zrozumienia i modyfikowania niż
złożony projekt charakteryzujący się wieloma ukrytymi odwołaniami utrudniającymi
przewidywanie zmian.
• Przyrostowe modyfikacje. Łatwiej uwzględniać jedną zmianę na raz zamiast wielu
zmian równocześnie. Kolejne zmiany powinny być integrowane z „linią bazową"
pojedynczo.
• Oswojenie ze zmianami. Zmiany są nieuniknione i mogą zdarzać się często — to
stwierdzenie traktowane jest jak norma, nie zaś jako wyjątek, którego chciałoby
się unikać.
• Wysoka jakość. XP koncentruje się na szybkich projektach, których postęp demon-
strowany jest bardzo często. Nie może to się jednak odbywać ze szkodą dla jakości
produktów: każda zmiana powinna być implementowana starannie i kompletnie,
zainwestowanie czasu na tym etapie pozwoli na uniknięcie jego straty w związku
z późniejszymi przeróbkami.

Zobaczmy teraz, jak ma się metodologia XP do planowania, wielokrotnego wykorzysty-


wania rozwiązań, modelowania, procesów i kontroli.

Ile planowania?

Podstawą planowania w XP są wymagania i ich relatywne priorytety. Wymagania zbie-


rane są w formie zapisywanych historii klienta. Owe „historie" to wysokopoziomowe przypadki
użycia obejmujące zbiór spójnych cech; programiści dekomponują następnie każdą historię
na zadania realizujące poszczególne cechy. Programiści szacują długość trwania każdego zada-
nia w dniach; jeśli dla danego zadania długość ta okaże się większa niż kilka tygodni, zadanie
16.4. Spektrum metodologii 733

takie kwalifikuje się do dalszej dekompozycji na prostsze zadania. Gdy oszacowana zostanie
już pracochłonność poszczególnych historii w kategoriach zadań do wykonania, klient i pro-
gramiści spotykają się w celu określenia kolejności realizacji tych historii, co przekłada się na
kolejne wersje systemu dostarczane klientowi do wdrożenia. Historie użytkownika organizo-
wane są w stos odzwierciedlający powiązania funkcjonalne między nimi. Klient dokonuje okre-
ślenia priorytetów poszczególnych historii, dzięki czemu najpierw realizowane będą najważ-
niejsze wymagania; realizację wymagań opcjonalnych można odłożyć na później. I tak właśnie
powstaje plan emisji: poszczególne historie wiązane są z przyszłymi wersjami systemu, dla
każdej wersji ustalany jest termin jej wdrożenia. Kolejne wersje emitowane są dość często,
zwykle co miesiąc lub dwa, co zapewnia szybkie sprzężenie zwrotne programistów z klientem
i użytkownikami.
Wysiłek niezbędny do realizacji historii użytkownika mierzony jest początkowo w ideal-
nych tygodniach. Idealny tydzień to tydzień roboczy, w którym cały wysiłek dedykowany jest
jednemu i tyko jednemu celowi. Jest oczywiste, że idealny tydzień dłuższy jest zwykle od kalen-
darzowego tygodnia roboczego, programiści bowiem muszą uczestniczyć w zebraniach, chorują,
przebywają na urlopach i tak dalej, a ponadto muszą zmagać się z wieloma niepewnościami,
nieoczywistościami i tym podobnymi przypadkami w projekcie. Dla nowych zespołów,
niemających doświadczeń z metodologią XP, zalecamy przyjęcie idealnego tygodnia w wymiarze
trzech kalendarzowych tygodni roboczych; w miarę zdobywania doświadczenia współczynnik
ten będzie się zapewne obniżał. Odwrotność współczynnika wyrażającego długość idealnego
tygodnia w tygodniach kalendarzowych, wyrażająca ilość tygodni idealnych realizowanych
w tygodniu roboczym, zwana jest potocznie szybkością projektu (project velocity).
Każda emisja systemu powstaje w wyniku kilku iteracji, trwających po dwa lub trzy
tygodnie. Każda z iteracji skutkuje promocją systemu, która przekazywana jest klientowi do te-
stów akceptacyjnych. W ten sposób klient, oglądając rozwój systemu w postaci niewielkich
przyrostów funkcjonalności, ma wielokrotnie okazję do żądania czy proponowania zmian.
Każda iteracja reprezentuje mały krok w stronę kolejnej emisji, ewentualne zmiany są więc
niewielkie i w razie potrzeby mogą być bez kłopotu wycofywane.
Każdą iterację poprzedza tak zwana „gra planistyczna", w ramach której klient wy-
biera historie do implementacji, przypisując każdej z nich priorytet według własnego uznania.
Dla każdej historii klient określa także testy akceptacyjne w postaci scenariuszy weryfikują-
cych jej implementację. Testy te wykorzystywane będą także do testowania regresyjnego syste-
mu przed każdą iteracją.
Po zakończeniu każdej iteracji programiści sumują pracochłonność (w tygodniach ideal-
nych) wszystkich zrealizowanych historii (czyli tych, których implementacje przeszły testy
akceptacyjne) i, odnosząc je do rzeczywistego upływu czasu, obliczają wskaźnik szybkości
projektu. Dla następnej iteracji klient wybrać może tylko taki zbiór historii, których szacowana
łączna pracochłonność nie przekracza zrealizowanej w poprzedniej iteracji. Dokonując takiego
wyboru, klient musi rozstrzygać kompromisy między funkcj onalnością a kosztem, programiści
zaś zyskują okazję do obiektywnej wyceny pracochłonności implementowania poszczególnych
cech. Czas planowania iteracji to także konieczność uporania się z testami akceptacyjnymi, któ-
re nie zostały zaliczone przy zakończeniu poprzedniej iteracji. Jeśli szybkość projektu znacząco
odbiega od oryginalnego planu przez więcej niż dwie iteracje, zwoływane jest zebranie pla-
nistyczne z udziałem klienta i weryfikowany jest harmonogram poszczególnych emisji.
734 Rozdział 16, ® Wszystko razem, czyli metodologie

Z perspektywy zarządzania projektem, historie klienta mogą być traktowane jako pakiety
pracy w strukturze podziału pracy. Zasadniczą różnicą w stosunku do tradycyjnych projektów
jest jednak fakt, że oszacowania czasochłonności poszczególnych zadań dokonywane są przez
programistów, nie przez menedżera projektu.

Ile powtarzalności?

XP nie akcentuje wielokrotnego wykorzystywania rozwiązań jako kluczowej metody


zwiększania produktywności. Ponieważ jednocześnie XP nie deprecjonuje znaczenia wzorców
projektowych, programiści mają pełną swobodę wyboru najprostszego rozwiązania imple-
mentacji poszczególnych historii. Wzorce projektowe mogą być wykorzystywane na po-
trzeby refaktoryzacji, jeżeli okażą się przydatne do implementacji takiej czy innej zmiany. Pod
tym względem XP różni się od tradycyjnego podejścia: zamiast definiowanej a priori architek-
tury opierającej się na konkretnych wzorcach projektowych, mamy architekturą doskonaloną
w miarę realizowania kolejnych historii, w miarę jak prototyp ewoluuje w stronę kompletnego
systemu.

Ile modelowania?

XP minimalizuje dokumentowanie, zgodnie z założeniem, że mniej produktów finalnych


to mniej pracy i mniej okazji do popełniania błędów. Formalne dokumentowanie wiedzy ustę-
puje miejsca jej bezpośredniemu przekazywaniu między uczestnikami: klient spełnia funkcje
„chodzącej dokumentacji", projekt systemu dyskutowany jest w ramach sesji z użyciem kart
CRC (patrz sekcja 5.4.5), a każdy programista może modyfikować dowolny fragment kodu.
Projekt systemu uwidoczniony zostaje w jak największym stopniu w kodzie źródłowym, po-
przez wybór oczywistego nazewnictwa oraz dekomponowanie złożonych metod na prostsze
o opisowych nazwach 2 . Częsta refaktoryzacja kodu przyczynia się do jego przyrostowego
doskonalenia, a uzgodnione standardy kodowania jeszcze bardziej pomagają programistom
komunikować się ze sobą na bazie jedynie kodu źródłowego.

Ile procesów cyklu życiowego?

XP realizuje prosty proces cyklu życiowego, składający się z czterech aktywności: plano-
wania, projektowania, kodowania i testowania. Planowanie odbywa się na początku każdej
iteracji. Aktywności projektowania, kodowania i testowania następują szybko po sobie w spo-
sób przyrostowy. Na wspomniane aktywności narzuconych jest jednak kilka ograniczeń.
• Najpierw testy. Implementowanie danej jednostki (modułu) poprzedzone jest stwo-
rzeniem dla niej testów jednostkowych. Z jednej strony, zapewnia to dostępność od-
powiednich testów dla ukończonego właśnie modułu, z drugiej, umożliwia progra-
mistom doskonalenie API tworzonej klasy.

2
Także poprzez obfite, lecz przemyślane komentowanie kodu w sytuacjach, gdy semantyka kodu
nie jest wystarczająco oczywista — przyp. tłum.
16.4. Spektrum metodologii 735

• Testy dla każdej nowej usterki. G d y w y k r y t a zostanie usterka, t w o r z o n e są zestawy


t e s t o w e z a p e w n i a j ą c e jej m a n i f e s t o w a n i e się w s p o s ó b p o w t a r z a l n y . G d y u s t e r k a
z o s t a n i e u s u n i ę t a , testy t e u r u c h a m i a n e są p o n o w n i e .

• Refaktoryzacja przed rozszerzeniem. G d y i m p l e m e n t o w a n e są n o w e cechy, r e f a k t o -


ryzacja k o d u m o ż e być n i e z b ę d n a d o s t w o r z e n i a p r o j e k t u p r z y s t o s o w a n e g o p o d tę
i m p l e m e n t a c j ę . Istniejący k o d jest n a j p i e r w r e f a k t o r y z o w a n y , a d o p i e r o p o t e m i m -
p l e m e n t o w a n e są jego rozszerzenia. W t e n s p o s ó b istniejący p r o j e k t c h r o n i o n y jest
p r z e d u s z k o d z e n i e m p r z e z i m p l e m e n t a c j e n o w y c h cech. J a k o że k o d jest w s p ó l n ą
własnością wszystkich p r o g r a m i s t ó w , p r o g r a m i s t a m o ż e r e f a k t o r y z o w a ć k o d s t w o -
rzony przez kogoś innego.

• Programowanie parami. P o d c z a s g d y poszczególni p r o g r a m i ś c i m o g ą s a m o d z i e l n i e


p i s a ć p r o t o t y p y dla e k s p e r y m e n t ó w l u b k o d w e r y f i k u j ą c y n o w e k o n c e p c j e , t o j u ż
k o d p r o d u k c y j n y — czyli b ę d ą c y częścią emisji — t w o r z o n y jest przez p a r y p r o g r a m i -
stów, co p r z y c z y n i a ć się m a d o lepszego p r o j e k t u , m n i e j s z e j liczby usterek, a także
przekazywania wiedzy między programistami.

• Częste integrowanie. Kod źródłowy tworzony przez poszczególnych programistów


i n t e g r o w a n y jest sukcesywnie z k o d e m b a z o w y m . D l a k a ż d e g o z i n t e g r o w a n e g o m o -
d u ł u p r z e p r o w a d z a n e są testy j e d n o s t k o w e , a c h a r a k t e r z e t e s t o w a n i a regresyjnego.

Jak widać, X P zachęca d o u s p r a w n i a n i a p r o c e s ó w cyklu życiowego, gdy jest t o n a p r a w d ę


użyteczne; nie p r z e w i d u j e j e d n a k ż a d n y c h aktywności d e d y k o w a n y c h t e m u u s p r a w n i a n i u a n i
jakiejkolwiek i n s t r u m e n t a c j i p r o c e s ó w — są o n e z b ę d n e w p r z y p a d k u niewielkich p r o j e k t ó w ,
n a potrzeby których w ogóle wynaleziono XP.

Ile kontroli?

X P zmierza d o m i n i m a l i z o w a n i a czasu, k t ó r y p r o g r a m i ś c i m u s z ą spędzać n a zebraniach,


b o w i e m zasadnicze ich obowiązki k o n c e n t r u j ą się w o k ó ł t w o r z e n i a zestawów testowych, i m -
p l e m e n t o w a n i a n o w y c h cech i r e f a k t o r y z o w a n i a k o d u . Dlatego status p r o j e k t u k o m u n i k o w a n y
jest w krótkich zebraniach n a p o c z ą t k u każdego d n i a roboczego: uczestnicy ustawiają się w k r ą g
i (na stojąco) składają raport na t e m a t zadań zakończonych p o p r z e d n i e g o dnia, żądań, n a d
k t ó r y m i aktualnie p r a c u j ą , i o t w a r t y c h p r o b l e m ó w , z k t ó r y m i m a j ą aktualnie d o czynienia. N i e
m a miejsca n a jakiekolwiek dyskusje — jeżeli takie są p o t r z e b n e , o d b y w a j ą się p ó ź n i e j w s p o s ó b
nieformalny, angażując jedynie osoby b e z p o ś r e d n i o uwikłane w ich problematykę.
Programowanie parami eliminuje potrzebę technik weryfikacyjnych w rodzaju prze-
g l ą d ó w p a r t n e r s k i c h czy inspekcji k o d u , b o r ó w n o w a ż n y p r o c e s p r o w a d z o n y jest n a bieżąco
w m i a r ę pisania k o d u . P o n i e w a ż k o d nie jest własnością tworzącej go pary, wielu p r o g r a m i s t ó w
lepiej z a p o z n a j e się z p o s z c z e g ó l n y m i jego f r a g m e n t a m i . O d p o w i e d n i a rotacja p a r sprzyja lep-
szemu upowszechnianiu informacji o projekcie.
C h a r a k t e r y s t y c z n a dla d u ż y c h p r o j e k t ó w h i e r a r c h i c z n a s t r u k t u r a o p i e r a j ą c a się n a
p o l e c e n i a c h i k o n t r o l i — jak ta p r z e w i d y w a n a p r z e z m e t o d o l o g i ę Royce'a — n a g r u n c i e X P
z a s t ą p i o n a została p r z e z system s a m o o r g a n i z u j ą c y się, o p i s a n y p r z e z J. H i g h s m i t h a i O . O r r a
736 Rozdział 16, ® Wszystko razem, czyli metodologie

[Highsmith i Orr, 2000]. Kierownik zespołu (a nie menedżer projektu) komunikuje wizję
wymagań dla systemu i jego architektury, zachęca zarówno do wykorzystywania nowych
technologii i nowych pomysłów, jak również do uwzględniania kryjących się za nimi czynni-
ków ryzyka. Kierownik kultywuje także środowisko, w którym programiści jego zespołu mogą
efektywnie współpracować, sprawnie wymieniać informację i zdobywać wzajemne zaufanie;
niekiedy jednak musi narzucać swe decyzje, choć powinien wykorzystywać możliwości budo-
wania konsensusu. Kierownik zespołu XP porównywalny jest do Polinezyjczyka nawigatora,
kierującego swe kanu ku wyznaczonemu celowi, bez uprzedniego planu wyznaczającego a priori
kolejne kroki podróży.

Podsumowanie

Ujęcie metodologii XP w podziale na kategorie zagadnień projektowych widoczne jest


w tabeli 16.4.

Tabela 16.4. Metodologia XP w podziale na kategorie zagadnień

Zagadnienie Metody
Planowanie Spisywanie historii klienta
Planowanie niewielkich, częstych emisji (co miesiąc lub dwa)
Tworzenie h a r m o n o g r a m u emisji
Planowanie i uruchamianie iteracji

Modelowanie Wybór najprostszego projektu dla realizacji bieżącej historii klienta


Wykorzystywanie metafor do objaśniania skomplikowanych koncepcji systemu
Wykorzystywanie kart CRC jako pomocy przy wstępnej dekompozycji systemu
na obiekty
Kodowanie zgodne z przyjętymi standardami
Refaktoryzowanie kodu, jeśli jest uzasadnione

Procesy Tworzenie zestawów testowych przed implementacją


Tworzenie testów jednostkowych dla każdego m o d u ł u
Zaliczenie wszystkich testów jako warunek emisji
Tworzenie przypadków testowych dla każdej wykrytej usterki
Opóźnienie dodawania nowych funkcji
Ścisła współpraca z klientem
Tworzenie kodu produkcyjnego przez pary programistów
Integrowanie cząstkowego kodowania z kodem bazowym

Kontrola Wspólna własność kodu


Adjustowanie h a r m o n o g r a m u
Rotacja par
Zebranie robocze na początku dnia
Częste testowanie akceptacyjne i publikowanie jego rezultatów
16.4. Spektrum metodologii 737

16.4.3. Metodologie rugby


W roku 1986 H. Takeuchi i I. Nonaka opublikowali metodologię mającą rozwiązywać specy-
ficzne problemy związane z opracowywaniem nowych produktów [Takeuchi i Nonaka, 1986].
Nazwali ją po prostu „podejściem rugby", przez analogię do dynamiki tej gry, w której za-
wodnicy muszą zarówno biec, jak i przekazywać sobie piłkę. Swą metodologię przeciwstawiają
oni dotychczas obowiązującym metodologiom, które porównują do sztafety, czyli jedynego
biegnącego zawodnika niosącego przekazaną mu pałeczkę. Ich zdaniem, metodologie tej
drugiej kategorii są w wielu sytuacjach zbyt wolne, zbyt ociężałe i niezdolne do efektywnego
reagowania na zmiany.
Gdy w rugby zostaną naruszone zasady gry, na przykład nastąpi podanie ręką piłki
w przód, gra jest zatrzymywana i wznawiana w postaci formacji zwanej „młynem" (serum).
W inżynierii oprogramowania „zwinne" metodologie zalecają programistom analogiczne
zatrzymanie i przeanalizowanie sytuacji, gdy pojawia się konieczność zmian. Aby zminimali-
zować ryzyko przeoczenia niespodziewanych zmian, metodologie te proponują częstą wy-
mianę informacji między programistami w celu jak najwcześniejszego wykrycia potrzeby
wspomnianych zmian. Gdy zatem metodologie „wagi ciężkiej" dążą raczej do unikania per-
spektywy zmian, to z perspektywy „zwinnych" metodologii zmiany są normą i następować
mogą w regularnych odstępach czasu.
Opisywane w poprzedniej sekcji programowanie ekstremalne (XP) jest właśnie jednym
przykładów „zwinnej" metodologii, likwidującej powolność i biurokrację metod „wagi ciężkiej",
o czym piszą K. Beck i C. Andres [Beck i Andres, 2005]. Bazując na „podejściu rugby",
K. Schwaber opublikował w 1995 roku metodologię Serum [Schwaber, 1995]. Wśród innych
metodologii o podobnej genezie, powstałych w tym samym czasie, wymienić należy adaptacyjne
tworzenie oprogramowania (ASD — Adaptive Software Development, opisane przez J. Higs-
mitha i K. Orra [Highsmith i Orr, 2000]) i „krystaliczną czystość" (Crystal Clear), o której pisał
A. Cockburn [Cockburn, 2004]. W 2005 roku M. Fowler opublikował przegląd tych metodologii
[Fowler, 2005]. W roku 2000 grupa ekspertów XP przedyskutowała związek tej metodologii
z innymi metodologiami „wagi lekkiej". Właśnie wówczas zdecydowano opatrzyć owe meto-
dologie mianem „zwinnych" (agile), gdyż określenie „waga lekka" (light weight) kojarzyło się
zbyt negatywnie — z „półgłówkiem" (light-headed). Kluczowe założenia zwinnych metodologii
opublikowane zostały w dokumencie Agile Alliance Manifesto [AgileManifesto, 2001]. Należą
do nich między innymi:

• pierwszeństwo jednostek i interakcji przed procesami i narzędziami. Najważniejszym


elementem projektu programistycznego są ludzie i ich współpraca. Tworzenie opro-
gramowania postępuje bardziej efektywnie w ramach samoorganizujących się ze-
społów i społeczności niż w przypadku hierarchii sztywno wytyczających granice
kompetencji, procesów o ustalonych rolach i wykoncypowanych narzędzi.
• pierwszeństwo działającego oprogramowania przed obszerną dokumentacją. Celem
tworzenia oprogramowania jest produkowanie oprogramowania, nie dokumentacji.
• pierwszeństwo współpracy przed negocjowaniem kontraktów. Kontrakt z klientem jest,
oczywiście, ważny, nie może jednak stanowić substytutu sprawnej komunikacji. Pro-
gramiści współpracujący z klientami lepiej rozumieją ich potrzeby, a przy okazji po-
głębiają własną wiedzę.
738 Rozdział 16, ® Wszystko razem, czyli metodologie

• pierwszeństwo reagowania na zmiany przed realizacją planu. Realizacja dobrego


projektu programistycznego powinna rozpoczynać się sporządzeniem jego planu, co
jednak nie oznacza sztywnego trzymania się wytyczonego kierunku, niezależnie
od zmieniającej się rzeczywistości. W miarę realizacji projektu następuje przecież
coraz lepsze rozumienie dziedziny aplikacyjnej i dziedziny realizacyjnej, zmienia się
środowisko biznesowe, pojawiają się nowe technologie — okoliczności te wymagają
należytego reagowania.

W tej sekcji opiszemy — w pięciu kategoriach stosowanych w tym rozdziale — elementy


metodologii rugby niewymienione przy okazji opisu XP.

Ile planowania?

Zwinne metodologie zyskały złą reputację jako „unikające planowania" czy „niechętne
do zobowiązań w kwestii terminów i cech systemu". Prawda jest jednak zgoła inna: zwinne
metodologie szeroko korzystają z planowania, które jednak nie zostaje skupione na samym
początku projektu. Zwinne planowanie ma charakter iteracyjny i związane jest z doskona-
leniem oszacowań, w miarę jak stabilizują się wymagania, a zespoły zyskują coraz większe
doświadczenie. W deterministycznym modelu tworzenia oprogramowania SPMP tworzony
jest jednorazowo na samym początku, w „zwinnym" niedeterministycznym modelu SPMP opra-
cowywany jest iteracyjnie i przyrostowo, w postaci przystosowywanej do bieżącej sytuacji.
Określenie iteracyjne planowanie oznacza, że planista zawsze przygotowany jest na to,
że plan okaże się błędny i trzeba będzie go skorygować, zamiast wszelkimi sposobami próbo-
wać wtłoczyć projekt w jego sztywne ramy. Tego typu sytuacje mogą zdarzać się nawet w środku
realizacji projektu, na przykład wskutek pojawienia się nowych obiecujących technologii. Na
początku projektu klient może zażądać, by produkt finalny posiadał „takie to a takie" cechy,
lecz później może uznać, że odmienny zestaw wymagań czyni ów produkt bardziej atrakcyjnym.
Opisana niepewność odzwierciedlona jest także we wszelkich modelach szacowania czasu,
budżetu i tym podobnych. M. Cohn zaleca w związku z tym trzy rodzaje oszacowań: pierwsze,
dokonywane na początku projektu, ma na celu ustalenie jedynie rzędu wielkości poszczegól-
nych wskaźników. Na kolejnym poziomie szacunkowym powinny się już pojawić wielkości
obarczone błędem nie większym niż +25% i -10%. W dalszym ciągu trwania projektu błąd ten
może zmniejszyć się do + 10% i -5% [Cohn, 2006].
Ze zwinnym planowaniem wiąże się — oczywiście — zwinne szacowanie. Jedną z jego
metod jest tak zwany poker planistyczny (Planning Poker), opisany przez J. Grenninga [Gren-
ning, 2002]. Każdy z uczestników zebrania otrzymuje od moderatora zestaw kart opatrzonych
różnymi wartościami, powiedzmy 1, 2, 3, 5, 8, 13, 20, 40 i 100. Prezentowana jest lista cech
systemu, podlegających szacowaniu — mogą nimi być wymagania funkcyjne i pozafimkcyjne,
często prezentowane w kategoriach historii użytkownika lub scenariuszy. Dla każdej pozycji
z tej listy uczestnicy dyskutują jej trudność, po czym każdy (w tajemnicy przed innymi) wybiera
jedną kartę, prezentującą odpowiednią (jego zdaniem) wartość oszacowania. Na znak mode-
ratora wszyscy odkrywają swe karty i posiadacze skrajnych ocen proszeni są o uzasadnienie
swego wyboru (konsensus za pierwszym razem zdarza się bardzo rzadko). To wywołuje
kolejną dyskusję, po czym procedura się powtarza, aż do osiągnięcia konsensusu. By jego
osiągnięcie ułatwić, można stosować rozmaite techniki pomocnicze, na przykład przyznać
16.4. Spektrum metodologii 739

autorowi wymagania większą wagę głosu. Jako że zwinne planowanie ma charakter iteracyjny,
planiści poprawiają swe oszacowania w miarę nabywania lepszego rozumienia dziedziny
aplikacyjnej i lepszego rozeznania zespołu programistów.

Ile powtarzalności?

Zalety metodologii rubgy ujawniają się przy tworzeniu nowych produktów (w ramach
inżynierii pierwotnej), kiedy to główny nacisk położony jest nie na wielokrotne wykorzystywa-
nie gotowych komponentów, lecz raczej na wielokrotne wykorzystywanie zdobytej wiedzy.
Zdobywanie wiedzy może odbywać się zarówno na poziomie zespołu, jak i na poziomie
poszczególnych programistów. Zespoły szkolą swe umiejętności techniczne — w ramach
tutoriali i programów szkoleniowych — i umiejętności społeczne. Co więcej, zespoły powinny
zdobywać wiedzę dotyczącą zagadnień powierzonych innym zespołom; pozwoli to programi-
stom rozmawiać zrozumiałym językiem z przedstawicielami innych dziedzin aplikacyjnych,
co ma pierwszorzędne znaczenie z perspektywy zbierania wymagań i odkrywania nowator-
skich projektów.
Wiedza może być rozpowszechniana między zespołami także poprzez rotacyjny przy-
dział ekspertów do poszczególnych zespołów. Organizacja jako taka zdobywa swą wiedzą
w drodze konwersji doświadczeń nabytych przy kolejnych projektach na standardowe praktyki,
strategia ta okazuje się jednak mało skuteczna w sytuacji, gdy kolejne projekty nie mają powta-
rzalnego charakteru, a realizowane są w dynamicznie zmieniającym się środowisku. Zdaniem
Takeuchiego i Nonaki uczenie się na błędach jest bardziej skuteczne niż wyciąganie wniosków
z samych sukcesów, więc programiści i menedżer powinni przede wszystkim uczyć się na wła-
snych i cudzych błędach, o czym pisze H. Petroski [Petroski, 1992]. Dopuszczalność błędów
i pomyłek w realizacji projektu jest jednym z istotnych aspektów metodologii Serum — ważne
jest ich wczesne rozpoznanie, dające wystarczająco dużo czasu na ich naprawienie. Temu
właśnie celowi służą codzienne zebrania statusowe.

Ile modelowania?

Przez całą treść tej książki przewija się koncepcja modelowania jako klucza do rozwiązy-
wania problemów inżynierii oprogramowania. UML stał się jednym z dominujących języków
modelowania systemów informatycznych. Wykorzystuje się go podczas komunikacji z klien-
tem (w postaci modeli przypadków użycia), realizacji dynamicznego zachowania (w postaci
diagramów stanów i diagramów aktywności) i opisywania instalacji systemu (w postaci dia-
gramów wdrażania).
Wielokrotnie akcentowaliśmy konieczność zachowania spójności między poszczególnymi
modelami systemu (sekcja 4.3.3). Zdaniem S. Amblera modele powinno się jednak aktualizo-
wać dopiero wtedy, gdy „poważnie rozmijają się" [Ambler, 2002], a nie po każdej, nawet drobnej
zmianie wprowadzonej do jednego modelu. Zwinne modelowania zakłada ponadto, że ta sama
koncepcja może być modelowana na wiele sposobów, w związku z czym Ambler zaleca rów-
noległe tworzenie kilku modeli, przy użyciu prostszej notacji i prostszych narzędzi; przykładowo
metodologia Serum zakłada zapisywanie zadań na karteczkach przypinanych w odpo-
wiednim układzie do tablicy.
740 Rozdział 16, ® Wszystko razem, czyli metodologie

Szczególnie prostą techniką modelowania jest tak zwane prototypowanie papierowe,


w ramach którego użytkownicy proszeni są o symulowanie realistycznych zadań przy użyciu
„papierowej" wersji interfejsu, patrz praca D. A. Normana [Norman, 2002]. Wybrana osoba
odgrywa rolę komputera realizującego ten interfejs — istotne jest jedynie zewnętrzne zacho-
wanie fikcyjnego komputera, nie zaś konkretny sposób implementacji wspomnianego interfejsu.
Tego rodzaju prototypy okazują się szczególne przydatne przy testach użyteczności, najczę-
ściej jednak wykorzystywane są do testowania interakcji użytkowników z aplikacjami webowymi
przeznaczonymi dla przeglądarek W W W i urządzeń mobilnych. Użytkownicy przekazują
w ten sposób swe cenne spostrzeżenia na temat interfejsu, podczas gdy jego projekt znajduje się
jeszcze na etapie rozrysowywania. „Papierowe prototypy" mogą mieć różną formę fizyczną, na
przykład ręcznych szkiców czy zrzutów ekranu spreparowanych za pomocą odpowiednich
edytorów graficznych.
Aby zachowanie użytkowników było jak najbardziej naturalne, programiści często nie
informują ich o tym, że system nie został jeszcze zaimplementowany. Z perspektywy użytkow-
ników wszystko wygląda jak w prawdziwym systemie, tymczasem nie zdają sobie sprawy z faktu,
że pracują nie z rzeczywistym systemem, ale... z osobą znajdującą się w innym pomieszczeniu,
symulującą pracę tego systemu z poziomu swojego komputera, zwaną nie bez powodu „czaro-
dziejem z krainy Oz". Mimo całej mistyfikacji, udaje się w ten sposób zebrać pokaźną ilość
użytecznej informacji na temat zachowania i oczekiwań użytkowników, przydatnej w projek-
towaniu systemu. Technika ta użyteczna jest zwłaszcza wtedy, gdy użytkownicy komunikują
się z systemem w języku o rozbudowanej składni i semantyce (albo nawet w języku natural-
nym); patrz praca J. F. Kelly ego [Kelly, 1984].

Ile procesów cyklu życiowego?

Deterministyczny model procesów, zwany także „definiowanym modelem kontroli pro-


cesów", opisany przez A. O. Babatundę i W. H. Raya [Tunde i Ray, 1994], opiera się na zało-
żeniu, że wyniki danego procesu zależne są jedynie od danych wejściowych — stosując wielo-
krotnie te same dane wejściowe, otrzymywać będziemy każdorazowo tak sam wynik. Modele
deterministyczne sięgają swymi korzeniami początków XX wieku, kiedy to F. Taylor sfor-
mułował swe idee naukowego zarządzania [Taylor, 1911]. Zgodnie z tymi ideami, wszelkie
procesy bazujące na tradycyjnych heurystykach czy wskazówkach powinny zostać zastąpione
przez precyzyjnie zdefiniowane procedury. Filozofia ta, w połączeniu z rozwojem metod
statystycznych, stworzyła podwaliny pod kontrolę jakości i zarządzanie nią. Całościowe
zarządzanie jakością (TQM — Total Quality Management) to inne podejście z lat 80. ubiegłego
stulecia, zmierzające do tego, by procesy produkcyjne stały się deterministyczne poprzez
zredukowanie, a najlepiej całkowite wyeliminowanie elementów wariantowych, co w zamie-
rzeniu skutkować miało większą spójnością wytwarzanych produktów; patrz praca E. W.
Deminga [Deming, 1982]. Kaskadowy model cyklu życiowego oprogramowania może być po-
strzegany jako próba traktowania produkcji tego oprogramowania w sposób deterministyczny.
Próba takiego potraktowania procesów inżynierii oprogramowania prowadzi jednak do
projektów charakteryzujących się niekompletnymi produktami, utratą kontroli i w efek-
cie dostarczeniem klientowi produktu rozmijającego się z jego oczekiwaniami na skutek
zamrożenia wymagań. Dlatego w odniesieniu do wytwarzania oprogramowania lepiej spraw-
dza się model niedeterministyczny, zwany także „empirycznym modelem kontroli procesów",
16.4. Spektrum metodologii 741

opisany przez O. Babatundę i W. H. Raya [Tunde i Ray, 1994]. Jego rację bytu stanowi fakt, że
procesy programistyczne prezentują same z siebie zbyt dużą złożoność, by w pełni zrozumieć je
a priori, zwłaszcza w obliczu nieuchronnych, nieoczekiwanych i być może częstych zmian
w środowisku. Menedżerowie wykorzystujący modele niedeterministyczne przygotowani są
na takie nieoczekiwane zdarzenia i radzą sobie z nimi poprzez częste inspekcje prac w projekcie:
przykładowo w metodologii Serum planowanie kolejnego przebiegu („sprintu") odbywa się
w oparciu o doświadczenia ostatniej iteracji, w związku z czym przedefmiowywanie celów pro-
jektowych lub zmiana wymagań klienta uważane są za naturalne i w pełni dozwolone zabiegi.
Tak właśnie wygląda w praktyce elastyczność modelu empirycznego.

Ile kontroli?

W zwinnych metodologiach kontrolowanie procesów odbywa się na bazie koncepcji


zwanej subtelną kontrolą (subtle control), sformułowanej przez H. Takeuchiego i I. Nonakę.
Z jednej strony, zespół podejmuje zobowiązanie realizacji skomplikowanych celów, z drugiej
jednak, struktura i działalność tego zespołu opiera się z definicji na samoorganizacji, którą
wspomniani autorzy rozumieją jako:

• autonomię — każdy zespół podejmuje decyzje we własnym zakresie, niezależnie od


swego miejsca w hierarchii organizacyjnej.
• samookreśloność — każdy zespół ustanawia swe własne cele i wypełnia swe własne
idee niezależnie od aktualnych wzorców i wiedzy z zakresu dziedziny aplikacyjnej
i dziedziny realizacyjnej. Zespoły motywowane są do niestandardowego myślenia
i przygotowania na odkrywanie nowych pomysłów, nawet za cenę przełamywa-
nia schematów myślowych i formułowania sprzecznych celów.
• interdyscyplinarność — członkowie każdego zespołu wybierani są w taki sposób, by
zapewnić komplet umiejętności niezbędnych do realizacji powierzonych zadań.

Tak więc z jednej strony — duża swoboda, z drugiej — poważne zobowiązania. Swoboda
ta nie pozostaje jednak całkowicie poza kontrolą: menedżer ustanawia zestaw „punktów
kontrolnych" niezbędnych do tego, by efektem owej swobody nie stała się niestabilność, a w kon-
sekwencji kompletny chaos. Kontrola zespołu nie ma jednak nic wspólnego z odgórnie narzuco-
nymi sztywnymi schematami, lecz ma raczej charakter samokontroli — kontroli grupowej
dynamiki zespołu, selekcji członków zespołu na podstawie ich doświadczenia i energii oraz łą-
czenia w ramach jednego zespołu nowicjuszy z doświadczonymi programistami. Bardzo ważna
jest zdolność menedżera projektu do przewidywania błędów i radzenia sobie z nimi.
Aby szybko reagować na pojawiające się problemy, powinny być one identyfikowane jak
najszybciej. Temu właśnie służą codzienne zebrania Serum, w ramach których członkowie ze-
społów regularnie dzielą się informacją o postępie swych prac i napotykanych problemach.

Podsumowanie

W tabeli 16.5 przedstawiamy ujęcie metodologii rugby w kategoriach zagadnień


projektowych.
742 Rozdział 16, ® Wszystko razem, czyli metodologie

Tabela 16.5. Metodologie rugby w podziale na kategorie zagadnień

Zagadnienie Metody
Planowanie Planowanie projektu w kategoriach priorytetowej listy wymagań
wysokopoziomowych
Jednomyślne definiowanie celów bieżącej iteracji
Porozumienie co do ogólnego oszacowania pracochłonności; precyzyjne
szacowanie możliwe jest tylko dla bieżącej iteracji
Codzienne rewidowanie oszacowań, na podstawie dokonanego postępu
i identyfikacji nowych zadań

Modelowanie Szkice
Prototypy papierowe
Symulacje „czarodzieja z krainy Oz"

Wielokrotne Wielokrotne wykorzystywanie wiedzy, nie modeli


wykorzystywanie Wymiana ekspertów między projektami w celu upowszechniania wiedzy
gotowych rozwiązań w organizacji

Procesy Równoległa realizacja aktywności programistycznych


Integrowanie ukończonych elementów z produktami przeznaczonymi
do emisji
Powrót nieukończonych elementów do listy niezrealizowanych zadań

Kontrola Codzienne krótkie zebrania statusowe


Ocena tendencji za pomocą wykresów wygaszania
Przeglądy wyników iteracji przez klienta i właściciela produktu
Rotacja programistów między zespołami (jeśli wskazana) przed kolejną
iteracją

16.5. Analizy przypadku


Początkujący menedżerowie projektu wykazują tendencję do kurczowego trzymania się ustaleń
podjętych na początku projektu i niechętni są do rewidowania podjętych decyzji stosownie do
zaistniałych sytuacji. Ma to swe źródło głównie w braku doświadczenia w zakresie metod i tech-
nik zarządzania projektem. Menedżer ciągle czuje się jak nowicjusz, eksplorujący możliwości
zastosowań narzędzi i metod w wielowymiarowej przestrzeni parametrów projektu — w prze-
strzeni tej każdy projekt ma postać punktu lub podprzestrzeni rozciągającej się na dopuszczalne
zbiory wartości wspomnianych parametrów.
By w tych warunkach możliwe było zdobywanie doświadczenia, początkujący menedżer
powinien najpierw podjąć się zarządzania prostym projektem, na przykład realizowanym przez
zespół pięcioosobowy i skupiającym się na jednym parametrze, którym może być nieznane
wymaganie, nowa technologia, nowa metoda czy nowa metodologia. Stanowczo nie zalecamy
projektu o większej liczbie parametrów w postaci dużej liczby nieznanych wymagań, nieznanych
technologii eksperymentalnych czy pięćdziesięcioosobowego zespołu. W miarę zdobywania
doświadczeń możliwe będzie sukcesywne przechodzenie od projektów z dobrze zlokalizowa-
nymi zmianami do projektów o większej liczbie stopni swobody.
16.5. Analizy przypadku 743

Opiszemy w związku z tym trzy projekty, na zasadzie analizy przypadku, widziane wła-
śnie oczyma początkującego menedżera. Wszystkie realizowane były w rzeczywistości dla re-
alnych klientów, przez studentów uczestniczących w zaawansowanych kursach inżynierii
oprogramowania. Wspomniane projekty to:

• ATRACT (.Automated Test and RAtionale Capture Tool) — sześciomiesięczny projekt


zrealizowany w metodologii XP, mający na celu stworzenie infrastruktury testowej
dla oprogramowania middleware (patrz sekcja 16.5.1). Projekt obejmował jedną iterację
zbierania wymagań i trzy iteracje programistyczne.
• FRIEND (First Responder Interactive Emergency Navigational Database) — zrealizo-
wany w trakcie pięciu iteracji system reagowania na sytuacje nadzwyczajne (patrz
sekcja 16.5.2). Opiszemy 1. jego iterację jako pojedynczy projekt, po czym przed-
stawimy drugi projekt stanowiący kombinację wspomnianych pięciu iteracji.
• JAMES (Java Architecture for Mobile Extended Services) — rozproszony, dwuiteracyjny
projekt skupiający się na aplikacjach wykorzystujących karty inteligentne (patrz
sekcja 16.5.3).

Wszystkie projekty zrealizowane zostały przez studentów; weźmy pod uwagę, że w trakcie
ich realizacji studenci prawdopodobnie uczestniczyli także w innych kursach. Jest to sytuacja
podobna do spotykanej w organizacjach macierzowych, czyli takich, w których dana osoba
może być uczestnikiem wielu projektów równocześnie. Dla każdego z wymienionych projek-
tów określimy cele, środowisko, metody planowania, wielokrotne wykorzystywanie rozwiązań,
modelowanie, procesy i kontrolę oraz podsumujemy wyciągnięte wnioski.

16.5.1. Projekt XP; ATRACT

Celprojektu

Celem projektu było dostarczenie frameworku dla tworzenia i przeprowadzania testów


oprogramowania middleware przeznaczonego dla małych urządzeń bezprzewodowych,
takich jak telefony komórkowe czy mobilne odtwarzacze MP3. Framework ten powinien
umożliwiać przesyłanie plików między urządzeniami. Jako że obsługiwanych ma być wiele
różnych, nowych typów urządzeń i wiele różnych typów plików, dostawca middleware zgłasza
zapotrzebowanie na środowisko ułatwiające tworzenie wspomnianych testów i automatyzujące
ich przeprowadzanie. Ponieważ w tworzeniu każdego z testów uczestniczyć może wielu pro-
gramistów, pożądana jest także możliwość dyskutowania poszczególnych testów przy użyciu
modelu zagadnień.
Celem 1. iteracji było wyprodukowanie niezawodnego środowiska testowego prezentują-
cego przynajmniej minimalną część funkcjonalności oraz dostarczenie dokumentacji opi-
sującej zasady konstruowania i prowadzenia testów. Dodatkowym celem dla wszystkich
uczestników było praktyczne poznanie metodologii XP jako środka realizacji małych projektów
i ocenienie jej efektywności w odniesieniu do programowania parami.
744 Rozdział 16, ® Wszystko razem, czyli metodologie

Środowisko projektu

Lokalny samodzielny klient. Rolę klienta pełnił dostawca oprogramowania, znający za-
równo technologię, jak i platformę.
Nowicjusze. Z klientem współpracowali dwaj programiści i ich nadzorca. Uczestniczyli
już w projektach programistycznych, nie mieli jednak żadnych doświadczeń z XP; wspomagał
ich więc zewnętrzny uczestnik doradzający w kwestiach zastosowania i przystosowywania
tej metodologii.
Dojrzała technologia. Ponieważ platformą dla systemu był język Java, programiści mieli
do wyboru bogatą ofertę dojrzałych komponentów.
Projekt zlokalizowany w siedzibie klienta. Programiści pracowali w styczności z klientem
i mieli nieograniczoną możliwość komunikowania się z nim. Klient mógł podejmować bez
konsultacji natychmiastowe decyzje.
Czas trwania projektu. Ponieważ programiści znali modelowanie przypadków użycia
i narzędzia związane z analizą wymagań, projekt podzielono na dwie fazy. Pierwsza, trwająca
dwa miesiące, ukierunkowana była wyłącznie na wymagania, zbiór pionowych prototypów
i model przypadków użycia. Stworzone przez programistów makiety i prototypy weryfikowa-
ne były przez klienta w kontekście modelu przypadków użycia. Druga faza, obliczona na cztery
miesiące, dedykowana była realizacji pierwszej emisji systemu według metodologii XP. Na po-
czątku tej fazy model przypadków użycia skonwertowany został na zestaw historii użytkownika,
wykorzystywanych przez klienta i programistów do nadania priorytetów poszczególnym wy-
maganiom i dokonania niezbędnych oszacowań związanych z tymi wymaganiami.
Dwufazowa struktura projektu minimalizowała ryzyko związane z użyciem nowej meto-
dologii (XP), przez jej ograniczenie do fazy konstrukcyjnej.

Ile planowania?

„Kamienie milowe" projektu znane były od samego początku, wskutek ograniczeń na-
rzuconych na programistów i nadzorcę. Poprzednie doświadczenia wyniesione z podobnych
projektów (pół roku realizacji, kilku programistów, nowy klient) sugerowały czas trwania fazy
wymagań na 6 do 8 tygodni. Koniec tej fazy zbiegał się także z początkiem nowego roku ka-
lendarzowego, dzięki czemu łatwiej było zaplanować następną fazę (w kalendarzu klienta).
Zaplanowano drugą fazę i jej dekompozycję na trzy iteracje, ale tylko pod warunkiem zakoń-
czenia fazy pierwszej. Programiści od początku nadali projektowi duże tempo, potwierdzając
swą produktywność: realizacja idealnego tygodnia zajęła im w przybliżeniu 1,5 tygodnia robo-
czego. Ku zaskoczeniu nadzorcy i klienta, szybkość ta została utrzymana przez cały projekt.

Ile powtarzalności?

Początkowo programiści nie planowali skorzystania z jakichkolwiek gotowych pakietów,


ze względu na krótkotrwałość drugiej fazy. Po dobrym rozpoznaniu zarówno dziedziny aplika-
cyjnej (pod względem potencjalnej struktury przyszłego oprogramowania middleware podle-
gającego testowaniu), jak i historii klienta postanowili napisać własny interpreter języka
służącego między innymi do tworzenia skryptów testowych, ustawiania wartości zmiennych,
16.5. Analizy przypadku 745

specyfikowania pętli i manipulowania plikami. Język ten stawał się jednak coraz bardziej
skomplikowany, programiści zdecydowali więc o zarzuceniu pomysłu własnego interpretera
i wykorzystaniu gotowego interpretera klasy open source, bazującego na JavaScripcie. Zaletą
takiego wyboru, który notabene przywrócił planową realizację projektu, były dodatkowe
elementy funkcjonalne dla klienta oraz łatwiejsza nauka systemu dla jego użytkowników.

Ile modelowania?

W pierwszej fazie wymagania klienta reprezentowane były w postaci przypadków


użycia. Po rozpoczęciu drugiej fazy na kartach indeksowych zapisane zostały jedynie zadania
do wykonania i historie użytkownika, po przejściu na metodologię XP model przypadków
użycia został zarzucony. Cały projekt systemu i projekt obiektów uwidoczniony został w kodzie
źródłowym, w postaci sugestywnego nazewnictwa klas i metod oraz dodatkowych komentarzy.
Ze względu na krótki czas trwania fazy oraz to, iż za kod ten odpowiedzialna była tylko para
programistów, brak modelu nie stanowił żadnego problemu.

Ile procesów cyklu życiowego?

W 2. iteracji procesy XP — planowanie, refaktoryzacja, tworzenie testów, realizacja


historii użytkownika i ich testowanie — przeprowadzane były wyłącznie przez programistów.
Z powodu braku czasu klient nie spisywał osobiście historii użytkownika ani nie definiował te-
stów akceptacyjnych — wyręczyli go w tym programiści, w ramach kilku zebrań. Ze względu
na brak odchyleń od harmonogramu rola nadzorcy ograniczyła się do obserwowania progra-
mistów i tylko szczątkowego uczestnictwa w fazie wymagań.

Ile kontroli?

Programiści spotykali się ze swym nadzorcą krótko po zakończeniu każdej iteracji, przed
spotkaniami z klientem, by zademonstrować bieżący status kodu i omówić ewentualne nieroz-
wiązane problemy. Ponieważ projekt realizowany był zgodnie z początkowym harmono-
gramem, a po zakończeniu poszczególnych iteracji pozostawało niewiele otwartych zagadnień,
spotkania programistów z nadzorcą sprowadzały się głównie do doskonalenia jego wiedzy
z zakresu dziedziny aplikacyjnej. Kilka dni po zakończeniu każdej iteracji odbywały się spotka-
nia z klientem w celu przeprowadzenia testów akceptacyjnych emisji będącej produktem tej
iteracji.

Wynik

Projekt zakończył się terminowo, klient otrzymał żądane produkty — dokumentację,


skrypty instalacyjne, kod źródłowy i moduł wykonywalny systemu. W krótkiej fazie postreali-
zacyjnej nastąpiło uaktualnienie zaniedbywanego dotychczas modelu przypadków użycia pod
kątem nowych wymagań. Klient zadowolony był z wyników, a co ważniejsze, także z tego, że
mógł dokładnie przyglądać się całej realizacji projektu.
746 Rozdział 16, ® Wszystko razem, czyli metodologie

Wnioski

Szkolenie klienta. Mimo iż projekt realizowany był w siedzibie klienta, ten nie dyspono-
wał zasobami wystarczającymi do dedykowania odrębnemu uczestnikowi zdefiniowania historii
użytkownika i testów akceptacyjnych — organizacja była na to zbyt mała. W efekcie, klient nie
nauczył się o procesach projektu tak wiele, jak początkowo planowano. W tej sytuacji na pro-
gramistach spoczęło zebranie i zweryfikowanie stabilnego zbioru wymagań, na podstawie
zaledwie kilku spotkań z klientem na przestrzeni dwóch miesięcy obejmujących pierwszą fazę;
zadanie ułatwiły im rozmaite scenariusze, makiety i przypadki użycia. W przypadku dłużej
trwających projektów, znajomość podstawowych zasad XP przez klienta, dokonującego prze-
cież przypisywania priorytetów poszczególnym wymaganiom, jest czynnikiem krytycznym.
W ujęciu Royce'a, projekt XP musi angażować spójne grono udziałowców.
Edukowanie programistów w temacie procesów cyklu życiowego. XP wymaga udziału
doświadczonych programistów; doświadczenie pomaga im dokonywać wiarygodnych oszaco-
wań, lokalizować zmiany i decydować o tym, które problemy rozwiązywać mają samodzielnie,
a które przekazywać innym uczestnikom. ATRACT był projektem wystarczająco małym, by obaj
programiści posiadali wyczerpującą wiedzę na temat jego statusu. W większych projektach
upowszechnianie takiej (i innych) informacji odbywa się między innymi przez rotację par pi-
szących kod źródłowy, tak by początkujący programiści mieli okazję nabywać doświadczenia
we współpracy z ekspertami. Jednak należy być świadomym faktu, że uruchamianie dużych
projektów w metodologii XP, z udziałem wyłącznie nowicjuszy, skutkować będzie bardzo dłu-
gim czasem ich nauki, zanim ów projekt będzie można uznać za względnie stabilny.

16.5.2. Lokalny klient: FRIEND

Cel projektu

FRI END to system zarządzania sytuacjami kryzysowymi, przeznaczony do koordynowania


jednostek polowych uczestniczących w obsłudze zdarzenia. Gdy rozpoczynano projekt FRIEND
w 1992 roku, zarządzanie sytuacjami kryzysowymi opierało się w większości na papierowych
dokumentach. Pojawienie się nowych technologii — cyfrowej komunikacji bezprzewodowej
i urządzeń mobilnych na czele z osobistymi asystentami (PDA) — stworzyło potrzebę za-
stąpienia środków tradycyjnych środkami elektronicznymi. System FRIEND zaprojektowany
został z myślą o obsłudze szerokiej gamy sytuacji nadzwyczajnych, od drobnych wypadków
drogowych do pożarów i huraganów. Projekt podzielony został na pięć iteracji, polegających
na demonstrowaniu coraz bardziej złożonych prototypów. Wiele przykładów prezentowa-
nych w poprzednich rozdziałach książki, głównie w rozdziale 2., 4. i 5., zaczerpnęliśmy właśnie
z systemu FRIEND.
Każda z iteracji projektu realizowana była w ramach jednosemestralnego kursu dla pro-
gramistów. Głównym celem projektu było opracowanie demonstracyjnego prototypu systemu
FRIEND, celem pobocznym — zaznajomienie studentów z aktualnym stanem sztuki w zakresie
metod i narzędzi inżynierii oprogramowania. Mimo iż projekt FRIEND nie był ściśle wzo-
rowany na metodologii Royce'a, jego początek obejmował wiele elementów tej metodologii,
między innymi organizację, dobrze zdefiniowane procesy zmian i podział artefaktów na grupy.
16.5. Analizy przypadku 747

Środowisko projektu

Lokalny samodzielny klient. Klientem zamawiającym system FRIEND był szef lokalnej
policji, niemający początkowo żadnego pojęcia ani o inżynierii oprogramowania, ani o tech-
nologiach bezprzewodowych. Znał — oczywiście — doskonale dziedzinę aplikacyjną, mógł
także podejmować natychmiastowe decyzje dotyczące zakresu i kierunku projektu.
Nowicjusze. Realizacja projektu obejmowała około czterdziestu uczestników, o dosko-
nałych kwalifikacjach technicznych, lecz małym doświadczeniu w zakresie realizacji projektów
takiej skali. Dziedzina aplikacyjna była dla nich nowością.
Dostępność użytkowników. Klient pełnił także rolę użytkownika, ponadto inni wybrani
użytkownicy wykonywali testy akceptacyjne.
Stan technologii. System FRI END opierał się na technologiach bezprzewodowych i PDA,
które również stanowiły platformę dla prototypów. Mimo iż technologie te były już względnie
stabilne, były jednak nowością dla programistów, co wymagało ich dodatkowego szkolenia,
zanim zajęli się tworzeniem kodu produkcyjnego.
Lokalizacja projektu. Projekt skupiony był w jednej lokalizacji — wszyscy studenci mieli
dostęp do wspólnego laboratorium. Kontakt z klientem możliwy był „na telefon".
Czas trwania projektu. Początkowo projekt obejmował dwa miesiące przygotowań, po
których następowała czteromiesięczna iteracja rozwojowa. Po jej pomyślnym zakończeniu
zrealizowane zostały cztery dodatkowe projekty, których wynikiem była udoskonalona kon-
cepcja architektury systemu; ostateczny produkt zrealizowany został przez firmę spin o f f 3 .
Całość realizacji projektu zajęła trzy lata.

Ile planowania?

Projekt FRIEND był w istocie eksplorowaniem koncepcji, zatem funkcjonalność i platfor-


ma prototypów nie zostały szczegółowo zdefiniowane. „Kamienie milowe" zostały jednak
ustalone na samym początku. Ponieważ uczestnicy mieli nikłe pojęcie o projektach tej skali,
harmonogram projektu oparty został na modelu kaskadowym. Każda z aktywności: przetwa-
rzanie wymagań, projektowanie systemu, projektowanie obiektów i kodowanie przewi-
dziana była w przybliżeniu na cztery tygodnie.

Ile powtarzalności?

Zarówno dziedzina aplikacyjna (zarządzanie sytuacjami kryzysowymi), jak i wykorzy-


stywane w projekcie nowinki techniczne (asystent osobisty Apple Newton 4 , bezprzewodowy
modem Motoroli) były nowością dla studentów. W zakresie części systemu specyficznych dla
dziedziny aplikacyjnej możliwości wielokrotnego wykorzystywania rozwiązań były znikome,
gotowe pakiety „z półki" wykorzystano natomiast do realizacji interfejsu użytkownika i pod-
systemu sieciowego. Generalnie krótki czas realizacji projektu poważnie ograniczał zakres wy-
korzystywania gotowych komponentów i ich złożoność.

3
Spin off— wydzielone ze struktury akademickiej przedsiębiorstwo, którego celem jest komercjalizacja
wiedzy naukowej. Patrz http://pl.wikipedia.org/wiki/Spin_off_(zarządzanie) —przyp. tłum.
4
Patrz http://pl.wikipedia.org/wiki/Newton_(komputer) — przyp. tłum.
748 Rozdział 16, ® Wszystko razem, czyli metodologie

Ile modelowania?

Rezultatem projektu były trzy grupy artefaktów: klienckie, menedżerskie i systemowe.


Ta ostatnia kategoria odpowiadała wymaganiom, projektowi, implementacji i wdrożeniu
w rozumieniu metodologii Royce'a. Do zbioru klienckiego należały dokumenty znajdujące się
pod odpowiedzialnością klienta, sporządzone przez niego we współpracy z menedżerem pro-
jektu, między innymi deklaracja problemu i wstępny harmonogram (patrz tabela 16.6).

Tabela 16.6. Artefakty klienckie systemu FRIEND. Studenci otrzymali gotową deklarację problemu

Dokument Przeznaczenie Wytworzony w wyniku


Deklaracja problemu Opisuje potrzeby, scenariusze Eksploracji koncepcji
wizjonerskie, środowisko docelowe,
wymagania i ograniczenia

Wstępny h a r m o n o g r a m Definiuje główne „kamienie milowe" Inicjacji projektu

Modele zadań, reprezentujące informację menedżerską, stworzone zostały przez mene-


dżera w fazie inicjacji projektu i rewidowane były w trakcie jego realizacji. Modele te udo-
kumentowane zostały w postaci planu zarządzania projektem, planu zarządzania konfiguracją
i projektu wysokopoziomowego (patrz tabela 16.7).

Tabela 16.7. Dokumenty menedżerskie systemu FRIEND

Dokument Przeznaczenie Wytworzony w wyniku


Projekt wysokopoziomowy Opisuje wstępną architekturę Projektowania wysokiego
oprogramowania, między innymi poziomu
dekompozycję na podsystemy
Plan zarządzania projektem D o k u m e n t kontrolny zarządzania Inicjowania projektu, jego
(SPMP) projektem monitorowania i kontroli

Plan zarządzania D o k u m e n t k o n t r o l n y opisujący Zarządzania konfiguracją


konfiguracją (SCMP) aktywności związane z zarządzaniem oprogramowania
konfiguracją oprogramowania

Modele systemu reprezentowały różne poziomy abstrakcji i odmienne punkty widzenia:


model przypadków użycia odzwierciedlał punkt widzenia użytkownika, model analityczny
opisywał encje modelujące domenę aplikacyjną, modele projektu systemu i projektu obiektów
reprezentowały architekturę oprogramowania i obiekty realizacyjne bazujące na wybranej
technologii — PDA i komunikacji bezprzewodowej. Model analityczny udokumentowany
był w formie dokumentu analizy wymagań (RAD) — patrz tabela 16.8.
Projekt systemu udokumentowany został w dokumencie SDD, zaś projekt obiektów
— w dokumencie ODD. Oba modele, (a także modele analityczne) sporządzone zostały w no-
tacji OMT, opisanej przez J. Rumbaugha, M. Blahę, W. Premerlaniego, F. Eddy'ego oraz W.
Lorensena [Rumbaugh i in., 1991], i przetworzone za pomocą niewielkiego narzędzia CASE
o nazwie OMTool. Modele OMT użyte zostały zarówno do wygenerowania fragmentów kodu
źródłowego, jak i diagramów w dokumentach projektu.
16.5. Analizy przypadku 749

Tabela 16.8. Dokumenty systemowe systemu FRIEND

Dokument Przeznaczenie Wytworzony w wyniku


D o k u m e n t analizy wymagań Opisuje globalne wymagania dla Analizy wymagań
(RAD) systemu i jego elementy funkcjonalne
w postaci modeli: przypadków użycia,
obiektowego, f u n k c j o n a l n e g o
i dynamicznego

Podręcznik użytkownika Opisuje zasady użytkowania systemu Analizy wymagań


(w formie tutorialu)

D o k u m e n t projektu systemu Opisuje cele projektu i kompromisy Projektowania systemu


(SDD) rozstrzygane w związku z konfliktami
między nimi, wysokopoziomową
dekompozycję systemu, identyfikację
współbieżności, platformę sprzętową
i programową, zarządzanie danymi,
globalne zarządzanie zasobami, model
sterowania i warunki brzegowe

D o k u m e n t projektu Opisuje system w kategoriach Projektowania obiektów


obiektów (ODD) udoskonalonego modelu
obiektowego, w szczególności
wykorzystywanych algorytmów
i struktur danych oraz sygnatur
wszystkich metod publicznych;
dokument ten jest p u n k t e m wyjścia
dla szczegółowej specyfikacji każdej
klasy na etapie implementacji

Podręcznik testera Opisuje strategię testowania — testy Testowania


jednostkowe i testy systemu wraz
z oczekiwanymi rezultatami

Podręcznik administratora Opisuje procedury administracyjne: Instalowania


instalowanie, odinstalowywanie, oprogramowania
pielęgnowanie, uruchamianie
i zamykanie systemu; zawiera także
listę kodów możliwych błędów oraz
rozpoznanych sytuacji wyjątkowych

P o d s t a w o w y m z a d a n i e m m o d e l i s y s t e m u FRIEND było p i e r w o t n i e wsparcie dla p r o j e k t o -


w a n i a i k o m u n i k a c j i . J a k o że c e l e m s y s t e m u FRIEND b y ł o p r e z e n t o w a n i e p r o t o t y p ó w , s p o -
dziewaliśmy się szybkiej jego ewolucji, k t ó r a uczyniłaby niezwykle k o s z t o w n y m u t r z y m y w a n i e
s p ó j n o ś c i m i ę d z y m o d e l a m i a s a m y m s y s t e m e m . N i e p r z y k ł a d a l i ś m y więc większej wagi d o
archiwizowania, ważniejsze okazało się u p o w s z e c h n i a n i e w i e d z y za p o ś r e d n i c t w e m p r o t o t y -
p ó w o r a z w i e d z y w n o s z o n e j p r z e z s t u d e n t ó w p r z y o k a z j i realizacji p r o j e k t u .
750 Rozdział 16, ® Wszystko razem, czyli metodologie

Ile procesów cyklu życiowego?

Model cyklu życiowego obejmował trzy fazy. W fazie prerealizacyjnej opracowane zostały
wstępne wymagania i wstępny kształt architektury. Dokonano także wyboru infrastruktury,
między innymi studenci otrzymali własne pomieszczenie przeznaczone wyłącznie do pracy
nad projektem. W tę fazę włączeni zostali dodatkowo klient, menedżer projektu i trenerzy. Na
początku fazy realizacyjnej studenci zostali podzieleni na zespoły pracujące nad konstrukcją
systemu. Celem fazy realizacyjnej była walidacja architektury systemu i uświadomienie uczest-
nikom wszystkich aspektów iteracyjnego cyklu życiowego oprogramowania. Aktywności
przetwarzania wymagań, projektowania, implementowania i tworzenia przypadków testowych
prowadzone były przyrostowo. Opracowana została także podstawowa funkcjonalność systemu
— jej opcjonalne elementy dodane zostały w fazie postrealizacyjnej. Powodem takiego podej-
ścia był zamiar terminowego dostarczenia systemu, z ograniczoną (choć spełniającą wymagane
minimum) funkcjonalnością zamiast spóźnionego dostarczenia kompletnego systemu. W ta-
beli 16.9 zestawione są trzy fazy modelu cyklu życiowego projektu FRIEND w terminologii
standardu IEEE 1074.
Zauważmy, że cykl życiowy nie obejmował aktywności operacyjnych ani aktywności
wsparcia technicznego, ponieważ celem projektu było opracowanie jedynie prototypów
demonstracyjnych.

Ile kontroli?

Ze względu na dużą liczbę uczestników (czterdziestu dwóch studentów i pięciu tre-


nerów) i braku ich doświadczenia w projektach o dużej skali, przyjęliśmy hierarchiczną
organizację i ścisłą kontrolę. Opracowany w fazie prerealizacji wstępny projekt wysokopo-
ziomowy stanowił bazę do podziału studentów na zespoły (patrz rysunek 16.2). Sformowano
pięć zespołów funkcyjnych (Campus, Communication, Database, EMOC i Ul) zajmujących
się poszczególnymi podsystemami. Łącznicy wybrani z każdego z tych zespołów utworzyli
międzyfunkcyjny zespół architektoniczny (.Architecture). Zespół narzędziowy (Tools) od-
powiedzialny był za dostarczanie innym zespołom elementów infrastruktury, między innymi
tworzenie skryptów, zarządzanie konfiguracją i budowanie środowiska testowego. Podstawę
podziału studentów na zespoły stanowiła macierz kwalifikacji, uwzględniająca wiedzę progra-
mistyczną, znajomość specyficznych narzędzi, życzenia i zainteresowania; informacji tych
dostarczyli sami studenci.
W pierwszym tygodniu projektu szkoliliśmy studentów w kwestii stosowania zdefi-
niowanych procesów raportowania statusu i przeprowadzania przeglądów. Każdy zespół
organizował co tydzień własne zebrania statusowe, prowadzone przez trenera, i ogłaszane
z wyprzedzeniem w formie agendy. Student pełniący rolę protokolanta miał obowiązek
zapisywania przebiegu zebrania i krótko po jego zakończeniu publikowania tego zapisu.
Trenerzy poszczególnych zespołów także spotykali się co tydzień z menedżerem projektu.
Postęp w realizacji projektu oceniany był w ramach comiesięcznych zebrań projektowych.
W pierwszym miesiącu menedżer i trenerzy rygorystycznie przestrzegali zarówno metod
raportowania statusu, jak i prowadzenia zebrań. Dzięki temu status projektu widoczny był
dla wszystkich studentów, co pozwalało im docenić sens wspomnianych rygorów. Ponadto
16.5. Analizy przypadku 751

Tabela 16.9. Aktywności cyklu życiowego projektu FRIEND

Odniesienie
Faza Cel Aktywności
do IEEE 1074
Prerealizacyjna Inicjacja projektu Inicjowanie projektu 3.1 Inicjowanie
projektu
Eksplorowanie koncepcji 4.1 Eksploracja
koncepcji
Projektowanie 4.2 Alokacja systemu
wysokopoziomowe

Realizacyjna Walidacja architektury Zarządzanie projektem 3.2 Monitorowanie


i kontrolowanie
projektu
Szkolenie uczestników Zbieranie wymagań 5.1 Przetwarzanie
Prezentowanie spełnienia Analizowanie wymagań wymagań
podstawowych wymagań
Prezentowanie Przegląd analizy wymagań
opłacalności Projektowanie systemu 5.2 Projektowanie
Przegląd projektu systemu
Projektowanie obiektów 5.2.7 Projektowanie
Przegląd projektu obiektów szczegółowe

Implementowanie 5.3 Implementacja


Testowanie jednostkowe 7.1 Weryfikacja
Testowanie integracyjne i walidacja
systemu
Alfa-testowanie systemu
Zarządzanie konfiguracją 7.2 Zarządzanie
konfiguracją
oprogramowania
Opracowywanie tutoriali 7.4 Szkolenie
Postrealizacyjna Prezentowanie spełnienia Doskonalenie 3.2 Monitorowanie
wymagań opcjonalnych zarządzania projektem i kontrolowanie
projektu
Dostarczenie prototypu 6.3 Pielęgnacja
Akceptacja klienta Instalowanie systemu 6.1.5 Instalowanie
oprogramowania
Testowanie akceptacyjne 6.1.6 Akceptacja
systemu przez klienta oprogramowania
Beta-testowanie 7.1 Weryfikacja
i walidacja
Zarządzanie konfiguracją 7.2 Zarządzanie
konfiguracją
oprogramowania
752 Rozdział 16, ® Wszystko razem, czyli metodologie

Rysunek 16.2. Zespołowa organizacja projektu FRIEND w ramach 1. iteracji (Carnegie Mellon University,
kurs 15-413, jesień 1992). Białe prostokąty reprezentują zespoły podsystemów, szare — zespoły między-
funkcyjne. Liczby w nawiasach oznaczają liczebność zespołów

w czasie pierwszych dwóch miesięcy role w każdym zespole zmieniały się rotacyjnie, umożli-
wiając każdemu studentowi pełnienie roli facilitatora, łącznika architektonicznego i narzę-
dziowca. Rotacja ta miała zarówno cel pedagogiczny — każdy student praktycznie zapoznawał
się z większością ról — jak i selekcyjny: większość studentów spisujących się doskonale w danej
roli pełniło tę rolę do końca projektu.
Po drugim przeglądzie (przeglądzie projektu systemu) zarówno rygoryzm procesowy,
jak i zasady rotacji ról zostały rozluźnione. Studenci zostali zachęceni do zachowania tych
metody i narzędzi, które sprawdziły się w zastosowaniu i zarzucenia pozostałych. W efekcie
proces raportowania statusu pozostał prawie niezmieniony, lecz coraz więcej decyzji podej-
mowanych było lokalnie w miarę postępu w kodowaniu. Studenci wykazujący zdolności ko-
munikacyjne zostali obsadzeni w rolach łączników, studenci wykazujący kwalifikacje techniczne
przydzielani byli do zadań technicznych. Konflikty i problemy z interfejsami rozstrzygane były
w osobistych spotkaniach w laboratorium, jedynie poważniejsze problemy integracyjne powo-
dowały zwoływanie zebrań.
W rezultacie organizacja projektu ewoluowała ze ściśle hierarchicznej w stronę przy-
pominającej bardziej serię podprojektów XP dla każdego podsystemu. Taka organizacja była
jednak możliwa tylko dzięki wyraźnemu zdefiniowaniu koncepcji architektonicznej, czyli
w konsekwencji określeniu zakresu odpowiedzialności każdego zespołu.

Wynik

Owocem projektu stał się prototyp koncepcyjny demonstrujący, jak system informacji
geograficznej (GIS) może być wykorzystywany do organizowania i wyświetlania informacji
związanej z wypadkami. Sukces pierwszego projektu spowodował uruchomienie czterech
kolejnych iteracji eksplorujących rozmaite technologie i elementy funkcjonalności (patrz
rysunek 16.3).
16.5. Analizy przypadku 753

Rysunek 16.3. Iteracje projektu FRIEND

• Iteracja 2. koncentrowała się na konsolidacji uwag klienta względem przedstawionego


prototypu. Realizowana była przy udziale wybranych studentów, którzy pracowali nad
pierwszym prototypem i teraz kontynuowali iteracyjne procesy cyklu życiowego. Każdy
przegląd dotyczył nowego elementu funkcjonalnego integrowanego z prototypem.
• Iteracja 3. była realizacją „od zera" bardziej ambitnej deklaracji problemu — zakres
systemu rozszerzony został o funkcjonalność awaryjnego planu operacyjnego (Emer-
gency Operations Plan) dla katastrof wymagających koordynacji na szczeblu stanu.
Iterację tę realizował nowy zespół studentów, w większości nowicjuszy, dlatego ujęta
została w ramy modelu kaskadowego.
• Iteracja 4. obejmowała uwzględnienie uwag klienta w związku z poprzednią iteracją
oraz przegląd zagadnień technicznych, takich jak użycie sieci WaveLAN i modemów
telefonów komórkowych. Prototyp systemu zaprezentowany został z użyciem PDA
Apple Newton. Iterację realizowali (zgodnie z podejściem iteracyjnym) studenci wy-
brani spośród uczestników iteracji 3.
• Iteracja 5. obejmowała eksplorację technologii Distributed Objects firmy NeXTStep
jako middleware zarządzającego komunikacją między węzłami.

Po zakończeniu iteracji 5. system FRIEND stał się produktem komercyjnym, realizowa-


nym przez wydzieloną ze struktury akademickiej firmę spin-ojf.

Wnioski

Szkolenie klienta. Na początku realizacji projektu klient nie tylko nie miał pojęcia o in-
żynierii oprogramowania, ale nawet nie miał obycia z komputerem. Zainwestował jednak
sporo czasu w naukę bezpośredniej komunikacji z programistami; przy czwartym prototypie
potrafił już rozumieć i krytykować wysokopoziomowe diagramy klas. Było to miła niespo-
dzianka dla programistów, bowiem klienci nie uwzględniają zwykle w infrastrukturze projektu
elementu samokształcenia; w przypadku długoterminowych problemów jest ono korzystne za-
równo dla klienta, jak i dla programistów.
Szkolenie programistów w zakresie dziedziny aplikacyjnej. Studenci dość szybko na-
uczyli się podstawowych koncepcji dziedziny aplikacyjnej, jako że klient, osiągalny na każde
wezwanie, wyjaśniał im wszelkie niejednoznaczności i nieporozumienia. W przypadku odda-
lonego klienta konieczne byłoby opracowanie innych metod walidacji wymagań i starannego
śledzenia wątpliwości, wieloznaczności i zmian.
754 Rozdział 16, ® Wszystko razem, czyli metodologie

Zorganizowanie dedykowanego laboratorium. Projekt bardzo skorzystał na przydzie-


leniu studentom laboratorium, w którym mogli spotykać się, programować, przygotowywać
prezentacje, prowadzić prezentacje dla klienta, pić kawę i tym podobne. Duża tablica wykorzy-
stywana była do śledzenia bieżących problemów i statusu projektu, przyporządkowywania zadań
i często prezentowała informację bardziej aktualną niż miejscowa witryna WWW. Laborato-
rium stało się także forum nieformalnej wymiany informacji, dzięki której wcześnie wykrytych
(i szybko rozwiązanych) zostało wiele problemów projektowych i menedżerskich.
Utrzymywanie infrastruktury. W organizacji projektu wydzielony został zespół narzę-
dziowy, odpowiedzialny za znajdowanie nowych narzędzi programistycznych oraz istniejących
bibliotek klas i wspieranie w ten sposób infrastruktury projektu. Ponieważ członkowie zespołu
narzędziowego byli nowicjuszami — co notabene jest sytuacją typową w tego typu kursach —
mieli nikłe rozeznanie w kwestii wymagań infrastruktury wspierającej duży projekt. Co więcej,
ponieważ nie brali udziału w głównych pracach projektu, uważani byli przez innych jego
uczestników za mało wiarygodnych. W związku z tym, zalecamy inne rozwiązanie w zakresie
infrastruktury — powierzenie jej utrzymywania specjalnej grupie administracyjnej niezwiązanej
z konkretnym projektem. Taka stała grupa kumuluje swą wiedzę w związku z przewijającymi
się przez organizację kolejnymi projektami, zapewniając w ten sposób większy pożytek dla swej
organizacji. Z racji owego doświadczenia cieszy się też zwykle poważaniem ze strony uczestników
wspomnianych projektów.
Używanie zintegrowanych narzędzi kontroli wersji. Zdefiniowanie schematu kon-
troli wersji i zbudowanie związanej z tym infrastruktury w fazie prerealizacyjnej oznacza do-
starczenie zespołom konkretnych mechanizmów dla wczesnego wykrywania i rozwiązywania
konfliktów we wprowadzanych do kodu modyfikacjach. Na początku fazy prerealizacyjnej po-
przedzającej iterację 2. opisywanego projektu, w repozytorium kontroli wersji umieszczony zo-
stał prosty prototyp „Hello World" z towarzyszącymi mu skryptami generowania binariów
i testowania. Praktykę taką stosuje wiele firm programistycznych, między innymi Microsoft;
patrz praca M. A. Cusamany i R. W. Selby'ego [Cusumano i Selby, 1997].

16.5.3. Rozproszony projekt: JAMES


Cel projektu

Technologia kart inteligentnych stwarza wielorakie możliwości zastosowań, że wspo-


mnimy tylko o kartach płatniczych, identyfikatorach elektronicznych, automatach telefonicz-
nych, parkomatach i bankomatach, ochronie zdrowia czy dostępie do sieci. Innym ważnym
zastosowaniem kart inteligentnych może być świadczenie dodatkowych usług posiadaczom
samochodów, po ich zakupie u dealera.
Cele projektu JAMES były dwojakie: po pierwsze, pojawiła się potrzeba rozpoznania tech-
nologii JavaCard i jej ocena pod kątem możliwości używania tej samej karty przez wiele aplikacji,
co zredukowałoby liczbę kart, jakie ich posiadacz musi nosić przy sobie; po drugie, interesują-
cym zagadnieniem okazało się zbudowanie architektury umożliwiającej użytkownikom wybór
kombinacji zastosowań i dynamiczne jego przechowywanie na kartach inteligentnych. Umożli-
wiłoby to dealerom samochodów oferowanie nowych usług i zastosowań dla swoich klientów.
Dla zgłębienia opisanych zagadnień stworzono projekt JAMES, którego efektem było
zaprezentowanie prototypów trzech aplikacji wykorzystujących karty inteligentne: Travel, jako
systemu planowania tras i wspomagania podróży w postaci wyświetlanej na pulpicie dyna-
756 Rozdział 16, ® Wszystko razem, czyli metodologie

Rysunek 16.4. Iteracje projektu JAMES

z modelem kaskadowym, podobnie jak w projekcie FRIEND. Ustalone „kamienie milowe" na-
rzuciły ograniczenia na przetwarzanie wymagań, projektowanie systemu i jego implemento-
wanie. Dla odmiany iteracja 2. sama charakteryzowała się podejściem iteracyjnym — co miesiąc
prezentowane były prototypy coraz bogatsze funkcjonalnie. Zespoły w TUM i CMU pracowały
nad różnymi podsystemami, jednak w oparciu o tę samą architekturę.

Ile powtarzalności?

Prezentowane prototypy koncepcyjne musiały współpracować z systemem bazy danych


klienta, co w połączeniu z krótkim czasem trwania iteracji redukowało okazje do wykorzysty-
wania nowych gotowych komponentów, które nie wchodziły jeszcze w skład istniejącej infra-
struktury.

Ile modelowania?

Tworzenie systemów informatycznych w warunkach rozproszenia geograficznego ma


kilka zalet, z których najważniejszą bodaj jest wykorzystywanie specyficznych kombinacji
umiejętności uczestników, bez konieczności fizycznego ich przemieszczania w ramach or-
ganizacji. W projekcie JAMES znaczący transfer wiedzy dokonał się na początku drugiej fa-
zy realizacyjnej. W CMU większość studentów biorących udział w 3. iteracji rekrutowała się
spośród uczestników 1., jednakże w TUM, przy rozpoczynaniu 2. iteracji, nikt poza instrukto-
rem i jednym z trenerów nie miał pojęcia o systemie. Uczestnicy z TUM mieli jednak dostęp
do wszystkich produktów finalnych 1. iteracji za pośrednictwem bazy Lotus Notes i poczty
elektronicznej. W celu polepszenia możliwości komunikacyjnych między dwoma ośrodkami
dwóch uczestników przeprowadziło się z TUM do CMU na czas trwania 2. iteracji, natomiast
jeden z uczestników CMU przyjechał do Monachium na czas integracji i testów akceptacyjnych.
Podobnie jak w projekcie FRIEND, model systemu JAMES traktowany był jako podstawa
projektowania i komunikacji; archiwalna wartość modeli 1. iteracji była mocno ograniczona,
ponieważ nie były one spójne z bieżącą wersją prototypu. Studenci z TUM odtworzyli model
pierwszego prototypu w ramach inżynierii odwracającej, usuwając niespójności i uzupełniając
braki w istniejącej dokumentacji. Rezultatem tej aktywności, którą nazwaliśmy „inwentaryzacją"
{Inventory Analysis), stał się dokument zwany „dokumentem inwentaryzacji", opisujący kom-
ponenty pierwszego prototypu i status jego implementacji (patrz tabela 16.10).
16.5. Analizy przypadku 757

Tabela 16.10. D o k u m e n t inwentaryzacji

Ile procesów cyklu życiowego?

W s t o s u n k u d o p r o j e k t u FRIEND cykl życiowy p r o j e k t u JAMES w z b o g a c o n y został o 2. ite-


rację realizacyjną i aktywności związane z t r a n s f e r e m w i e d z y m i ę d z y o ś r o d k a m i (patrz tabela
16.11). O b i e iteracje realizacyjne m i a ł y taką s a m ą s t r u k t u r ę , w postaci takich s a m y c h r o d z a j ó w
d o k u m e n t ó w , m e t o d i p r o c e d u r ; j e d n a k ż e w 2. iteracji d o k u m e n t y R A D , S D D , O D D zostały
nieco ulepszone.

Tabela 16.11. Aktywności projektu JAMES. Różnice w stosunku do projektu FRI END wyróżniliśmy kursywą

Odniesienie
Faza Cel Aktywności
d o IEEE 1 0 7 4

Prerealizacyjna Inicjacja projektu Inicjowanie projektu 3.1 Inicjowanie


projektu
Eksplorowanie koncepcji 4.1 Eksploracja
koncepcji
Projektowanie 4.2 Alokacja systemu
wysokopoziomowe

Pierwsza faza Walidacja architektury Zarządzanie projektem 3.2 Monitorowanie


realizacyjna i kontrolowanie
projektu
Szkolenie uczestników Zbieranie wymagań 5.1 Przetwarzanie
w pierwszym ośrodku wymagań
Prezentowanie spełnienia Analizowanie wymagań
podstawowych wymagań
Prezentowanie Projektowanie systemu 5.2 Projektowanie
opłacalności
Projektowanie obiektów 5.2.7 Projektowanie
szczegółowe
Implementowanie 5.3 Implementacja
Testowanie jednostkowe 7.1 Weryfikacja
Testowanie integracyjne i walidacja
systemu
Alfa-testowanie systemu
Zarządzanie konfiguracją 7.2 Zarządzanie
konfiguracją
oprogramowania
Opracowywanie tutoriali, 7.4 Szkolenie
prowadzenie wykładów
758 Rozdział 16, ® Wszystko razem, czyli metodologie

Tabela 16.11. Aktywności projektu JAMES. Różnice w stosunku do projektu FRI END wyróżniliśmy kursywę
— ciąg dalszy

Odniesienie
Faza Cel Aktywności
d o IEEE 1 0 7 4

Druga faza Usprawnianie architektury Inwentaryzacja


realizacyjna Szkolenie uczestników Przegląd inwentaryzacji
w drugim ośrodku
Prezentowanie Zarządzanie projektem 3.2 Monitorowanie
rozszerzalności i kontrolowanie
projektu
Prezentowanie Analizowanie wymagań 5.1 Przetwarzanie
spełnienia wymagań Przegląd analizy wymagań wymagań
Projektowanie systemu 5.2 Projektowanie
Przegląd projektu systemu
Projektowanie obiektów 5.2.7 Projektowanie
Przegląd projektu obiektów szczegółowe
Implementowanie 5.3 Implementacja
Testowanie jednostkowe 7.1 Weryfikacja
Testowanie integracyjne i walidacja
Alfa-testowanie
Zarządzanie konfiguracją 7.2 Zarządzanie
konfiguracją
oprogramowania
Opracowywanie tutoriali 7.4 Szkolenie
Postrealizacyjmi Prezentowanie kompletnej Doskonalenie zarządzania 3.2 Monitorowanie
funkcjonalności projektem i kontrolowanie
projektu
Dostarczenie prototypu 6.3 Pielęgnacja
Akceptacja klienta Instalowanie systemu 6.1.5 Instalowanie
oprogramowania
Testowanie akceptacyjne 6.1.6 Akceptacja
systemu przez klienta oprogramowania
Beta-testowanie 7.1 Weryfikacja
i walidacja
Zarządzanie konfiguracją 7.2 Zarządzanie
konfiguracją
oprogramowania

Ponieważ projekt zależny był od technologii JavaCard, jeden z zespołów w T U M od-


p o w i e d z i a l n y był za o p r a c o w a n i e p r o t o t y p ó w pozwalających ocenić ryzyko związane z niesta-
b i l n y m j e s z c z e s t a n e m tej t e c h n o l o g i i . Z e s p ó ł t e n w s p ó ł p r a c o w a ł b e z p o ś r e d n i o z d o s t a w c ą
technologii, r a p o r t u j ą c m u n a p o t k a n e p r o b l e m y i p r e z e n t u j ą c w s p o m n i a n e p r o t o t y p y n a co-
miesięcznych p r z e g l ą d a c h z w i ą z a n y c h z w y m i e n i o n y m i d o k u m e n t a m i . P r o t o t y p y te w y k o -
rzystywane były później przez pozostałe zespoły jako przykładowy k o d p o m o c n y przy im-
p l e m e n t o w a n i u ich własnych podsystemów.
16.5. Analizy przypadku 759

Iteracja 2. przerodziła się więc w proces ukierunkowany na encje, czyli koncentrujący się
na prototypach, nierozwiązanych problemach i zmianach, a nie na aktywnościach (patrz sek-
cja 15.4) — w przypadku problemów związanych z środowiskiem JavaCard takie podejście
oznaczało po prostu mniejsze ryzyko.

Ile kontroli?

Podobnie jak projekt FRIEND, tak i projekt JAMES rozpoczął swój żywot od ściśle hierar-
chicznej organizacji. W trakcie 1. iteracji czterdziestu jeden uczestników podzielono na pięć ze-
społów (patrz rysunek 16.5) odpowiedzialnych za poszczególne podsystemy (Logbook, Mainte-
nance, Travel VIP i Vehicle). Dwa zespoły międzyfimkcyjne — Architecture i HCI — składające
się z łączników między zespołami funkcyjnymi —- odpowiedzialne były za koordynowanie
zagadnień związanych z (odpowiednio) architekturą systemu i interfejsem użytkownika. Każdy
zespół referował status swych prac i napotykane problemy w trakcie cotygodniowych zebrań.
Trenerzy zespołów spotykali się co tydzień z menedżerem projektu.

Rysunek 16.5. Zespołowa organizacja projektu JAMES w ośrodku w Pittsburgu w ramach 1. iteracji
(Carnegie Mellon University, kurs 15-413, jesień 1997). Białe prostokąty reprezentują zespoły pod-
systemów, szare — zespoły międzyfunkcyjne. Liczby w nawiasach oznaczają liczebność zespołów

W ramach 2. iteracji dwa zespoły funkcyjne — Bonus i VIP — zlokalizowane zostały


w TUM (łącznie dwudziestu trzech uczestników), zaś liczący dziesięciu uczestników zespół
Travel zlokalizowany został w CMU. Zespoły HCI i Architecture zlokalizowane zostały w TUM
i zaliczone w poczet zespołów funkcyjnych (nie międzyfunkcyjnych). Trenerzy zespołów spoty-
kali się co tydzień z menedżerem lokalizacji (site manager), który kontaktował się z menedże-
rem projektu. Menedżer projektu przemieszczał się wahadłowo między obydwoma ośrodkami,
przebywając w każdym z nich po trzy tygodnie, w celu lepszej koordynacji prac między
ośrodkami.
Tak samo jak w projekcie FRIEND, po dwóch miesiącach struktura projektu JAMES prze-
obraziła się ze ściśle hierarchicznej w iteracyjną. W 1. i 2. iteracji dało to podobne rezultaty
— studenci rozpoznali role, w których czuli się najlepiej, wymieniali swą wiedzę na zebraniach
i umieszczali na tablicy bieżącą informację o statusie swych prac. Jednak w 3. iteracji, ze
760 Rozdział 16, ® Wszystko razem, czyli metodologie

względu na małą liczebność uczestników, zrezygnowano z zebrań i znacznie ograniczono zakres


prezentowanej informacji o statusie. Studenci, pracujący w jednym pomieszczeniu, mogli bez-
pośrednio rozwiązywać większość problemów, jednakże kontakty z TUM stały się znacznie
mniej efektywne.

Wynik

Projekt JAMES zakończył się pomyślną prezentacją prototypu ilustrującego potencjalne


możliwości zastosowań technologii JavaCard w przemyśle samochodowym. Prototyp nie został
jednak zintegrowany: końcowe prezentacje w każdym z ośrodków odbyły się na bazie różnych
architektur — w CMU zwyczajnie zignorowano rewizje poczynione w TUM i kontynuowano
prace w gałęzi datującej się jeszcze sprzed 1. iteracji.
Przy okazji realizacji rozproszonego projektu staliśmy się bogatsi o jeszcze jedno doświad-
czenie, nieznane w warunkach skupionego projektu. Końcowe testy akceptacyjne uzgodniono
z klientem na dzień 31 marca 1998, na godzinę 9:00 czasu amerykańskiego, czyli na godzinę
15:00 czasu środkowoeuropejskiego, w formie trójstronnej wideokonferencji. Zbliżało się
przejście na czas letni, w nocy z 4 na 5 kwietnia; nie wiedzieliśmy jednak o tym, że Europejczycy
przechodzą na czas letni o tydzień wcześniej, w ostatnią niedzielę marca — co uświadomili
nam studenci z CMU przebywający w Monachium: gdy 31 marca będzie u nas godzina 15:00,
w Pittsburgu będzie ósma rano, nie dziewiąta: mało brakowało, a rozminęlibyśmy się z termi-
nem prezentacji o całą godzinę! Na szczęście, obyło się bez poważnych konsekwencji, jeśli nie
liczyć dodatkowej dawki stresu.

Wnioski

Edukowanie klienta. Współpraca z klientem okazała się trudniejsza, niż początkowo są-
dziliśmy, głównie z dwóch powodów.

• W trakcie 1. iteracji pełnomocnik klienta znajdował się w bezpośrednim kontakcie


ze studentami. Szybko zapoznał się z procesami projektu i jego infrastrukturą, lecz
często musiał kontaktować się ze swą organizacją w wielu kwestiach decyzyjnych.
I tu pojawiła się bariera hamująca postępy w projekcie: pełnomocnik znakomicie ro-
zumiał modele prezentowane mu przez studentów, musiał jednak poświęcać sporo
czasu na ich konwertowanie do poziomu abstrakcji zrozumiałego przez jego zwierzch-
ników. Problem ten złagodzono (niestety, dopiero pod koniec iteracji), opierając ra-
portowanie informacji na scenariuszach.
• W trakcie 2. iteracji pełnomocnik klienta znajdował się w swej macierzystej organizacji
i nie był fizycznie obecny w żadnym z ośrodków. Poczta elektroniczna i wideokonfe-
rencje stanowiły jedyny sposób kontaktu z nim. I choć dla zespołów z TUM stanowiło
to barierę w zakresie zdobywania niezbędnej wiedzy z zakresu dziedziny aplikacyjnej,
to jednocześnie fakt, iż pełnomocnik klienta znajdował się w pobliżu swych zwierzch-
ników, łagodził problemy opisane w poprzednim punkcie.
16.5. Analizy przypadku 761

Edukowanie programistów w temacie procesów cyklu życiowego. W przypadku


krótkich iteracji z udziałem studentów szybkie przyswojenie przez nich know-how związanego
z procesami cyklu życiowego jest czynnikiem krytycznym dla sprawnej organizacji. Począwszy
od 2. iteracji, trenerzy rekrutowali się spośród najlepszych uczestników iteracji 1., co istotnie
zaowocowało takim postępem. Trenerzy ci potrafili także efektywnie rozpowszechniać wiedzę
z dziedziny aplikacyjnej i szybko rozwiązywać napotykane problemy. Krotki czas trwania iteracji
i naturalna rotacja uczestników ograniczały jednak repertuar używanych narzędzi. Zarówno
bowiem zmiana narzędzi dla projektu, jak i zmiana samego projektu nie jest dla niego korzystna,
gdy dokonuje się w jego trakcie. Lepiej dokonywać jej między kolejnymi projektami (iteracjami),
a w przypadku długotrwałych iteracji — między kolejnymi emisjami produktów.
Nakładanie się iteracji. Stopień nakładania się kolejnych (sąsiednich) iteracji w projek-
tach rozproszonych to kolejny kompromis do rozstrzygnięcia przez menedżera. Mały stopień
„zazębiania" się iteracji to także niewielka szansa na przekazywanie wiedzy — uczestnicy kolej-
nej iteracji zyskują tę wiedzę jedynie na podstawie produktów iteracji poprzedniej. Duży
stopień nakładania się iteracji to większa sposobność do wymiany informacji w ramach ukie-
runkowanych pytań i odpowiedzi, lecz także niepożądana interferencja w proces, w którym
informacja ta dopiero powstaje.
Transfer wiedzy implikowanej. W przypadku rozproszonych projektów uczestnicy pra-
cujący w różnych ośrodkach raczej nie znają się osobiście, zatem nieformalna wymiana infor-
macji między nimi jest mało prawdopodobna. Wstrzymuje to znacznie spontaniczny przepływ
wiedzy, tak bardzo istotny dla rozwiązywania problemów. Jedynym antidotum na tę przypa-
dłość są ... wycieczki uczestników między ośrodkami.
Kontrola wersji jako narzędzie komunikacji. W systemie FRI END system kontroli wersji
spełniał rolę mechanizmu integracyjnego, także w zakresie komunikacji. W przypadku skupio-
nych projektów normalną rzeczą jest powstawanie nieformalnych wersji plików, transfe-
rowanych między zespołami głównie w celach eksperymentalnych, poprzedzające oficjalne
integrowanie tych plików z kodem bazowym. Taka nieformalna komunikacja przy użyciu
narzędzi kontroli wersji wykluczona jest w przypadku oddalonych ośrodków.

16.5.4. Podsumowanie analiz przypadku


Trzy zaprezentowane projekty różniły się pod wieloma względami: skali, czasu trwania i typu
klienta. Projekt ATRACT angażował czworo uczestników, projekt JAMES — ponad osiemdziesięciu.
Projekt ATRACT zrealizowany został w pół roku, realizacja projektu FRI END zajęła dwa lata. Zna-
cząco różni byli też klienci kontraktujący opisywane projekty — od szefa policji nieprzed-
stawiającego żadnego obycia z komputerem, a tym bardziej z technikami informatycznymi, po
specjalistów z departamentu informatyki światowego producenta samochodów. Podstawowe
cechy różnicujące wspomniane projekty przedstawiliśmy w tabeli 16.12.
W rezultacie w każdym projekcie wykorzystywane były różne metody zarządzania —
z różnym skutkiem. W kolejnych sekcjach spróbujemy przedyskutować heurystyki wy-
pływające z przedstawionych analiz przypadku, dotyczące planowania, modelowania, pro-
cesów i kontroli. W tabeli 16.13 zamieściliśmy podsumowanie decyzji podejmowanych w tych
obszarach w każdym z projektów; pominęliśmy zagadnienie wielokrotnego wykorzysty-
wania gotowych komponentów, jako że w opisywanych przypadkach miało ono znaczenie
marginalne.
762 Rozdział 16, ® Wszystko razem, czyli metodologie

Tabela 16.12. Cechy środowiska opisywanych analiz przypadku

Projekt Problem Typ klienta Czas trwania Rozproszenie

ATRACT Środowisko Klient lokalny, 6 miesięcy Lokalny klient


testowania samodzielny
middleware
FRIEND System reagowania Klient lokalny, 4 miesiące Lokalny klient
na sytuacje samodzielny (+ 20 miesięcy
nadzwyczajne kontynuacji)
(inżynieria
pierwotna i wtórna)

JAMES Zastosowanie kart Pełnomocnik klienta 11 miesięcy Zdalny klient,


inteligentnych realizacja w dwóch
w przemyśle ośrodkach
samochodowym

Tabela 16.13. Podsumowanie sposobów zarządzania opisanymi projektami

Projekt Planowanie Modelowanie Procesy Kontrola

Planowanie Przypadki użycia, Faza przetwarzania Prezentacje klienckie


o p a r t e n a XP, historie wymagań, następnie po zakończeniu
p o szczegółowym użytkownika trzy realizacyjne każdej iteracji
ATRACT rozpoznaniu iteracje XP
wymagań

Planowanie Klient, Model kaskadowy, Cotygodniowe


iteracyjne zgodnie zarządzanie, zbiór następnie cztery zebrania zespołów,
z metodologią artefaktów, O M T iteracje zgodne comiesięczne
FRIEND Royce'a z jednolitym spotkania z klientem
procesem

Planowanie Klient, Model kaskadowy, Cotygodniowe


iteracyjne zgodnie zarządzanie, zbiór następnie kilka zebrania zespołów,
z metodologią artefaktów, UML iteracji zgodnych comiesięczne
JAMES Royce'a z jednolitym przeglądy projektu,
procesem naprzemienna
obecność menedżera
p r o j e k t u w obu
ośrodkach,
inwentaryzacja

Ile planowania?

P l a n o w a n e „ k a m i e n i e m i l o w e " i zakres szczegółowości ich o p i s u zależne są o d celów


p r o j e k t u . W p r o j e k c i e ATRACT p o ś w i ę c o n o d w a miesiące n a o p r a c o w a n i e specyfikacji f u n k c j o -
n a l n e j u m o ż l i w i a j ą c e j klientowi i p r o g r a m i s t o m z r ó ż n i c o w a n i e p r i o r y t e t ó w poszczególnych
w y m a g a ń , p o n i e w a ż celem p r o j e k t u b y ł o s t w o r z e n i e narzędzia, k t ó r e klient zamierzał w y k o -
rzystywać w s w y m ś r o d o w i s k u p r o d u k c y j n y m . W 1. i t e r a c j i s y s t e m u FRIEND a n i k l i e n t , a n i
16.5. Analizy przypadku 763

programiści nie znani swych dziedzin wiedzy na tyle, by można było w ich kwestii cokolwiek
zaplanować. Zamiast tego uzgodniono daty przeglądów i prezentacji, pozostawiając duży mar-
gines elastyczności dla „kamieni milowych", których szczegóły przeznaczone były do uzupeł-
nienia w miarę postępów w projekcie i zdobywania niezbędnej wiedzy.
Generalnie nie powinno się podejmować szczegółowego planowania, zanim klient i pro-
gramiści nie uzgodnią elementów funkcjonalnych i architektury systemu. Jeśli czynniki te są
przedmiotem ciągłych zmian, szczegółowe ich planowanie możliwe jest co najwyżej w odnie-
sieniu do następnej iteracji. Tak czy inaczej konieczne jest włączenie klienta i programistów
w proces planowania oraz zapewnienie, że możliwości zmian w planowaniu zostaną wyraźnie
wyartykułowane.

Ile modelowania?

Zakres modelowania zależy od rozmiaru wiedzy, która musi zyskać jawną postać i do-
trzeć do wszystkich uczestników. W projekcie ATRACT uczestnicy konstruowali przypadki uży-
cia, aż do nabycia przekonania, że wystarczająco dobrze rozumieją wymagania klienta i że
klient jest w stanie stworzyć obraz systemu możliwego do realizacji. W dalszym ciągu projektu
modelowanie było już zbędne, bowiem wszyscy trzej uczestnicy pracowali nad projektem w tej
samej lokalizacji i efektywniejsza okazywała się nieformalna komunikacja między nimi.
W projekcie FRIEND skonstruowano szczegółowe modele dziedziny aplikacyjnej i doce-
lowego systemu; modele te wykorzystane zostały do generowania kodu. Dziedzina aplikacyjna
była dla programistów zdecydowanie bardziej obca niż w przypadku systemu ATRACT, większa
też była liczba uczestników, z myślą o większej szybkości realizowania projektu. Modele stano-
wiły głównie bazę dla projektowania i komunikacji, lecz nie były archiwizowane, ze względu na
zmieniającą się dynamicznie postać prototypów.
W rozproszonym projekcie JAMES, w związku z transferem wiedzy między ośrodkami,
objętość wiedzy, którą należało sprowadzić do jawnej postaci, była znacząco większa. Podobnie
jak w projekcie FRIEND, zrezygnowano z utrzymywania spójności między modelami systemu;
zamiast tego studenci odtworzyli model z 1. iteracji, w ramach inwentaryzacji. Tworzenie
szczegółowych modeli warte było zainwestowanego wysiłku, bo transferowano wiedzę między
kontynentami; było ono jednak znacznie utrudnione, ze względu na szybkie tempo zmian
i ograniczoność dostępnych zasobów. Co więcej, szczegółowe modelowanie odbierane było jako
zbędna aktywność — przynosząca korzyści tylko drugiemu ośrodkowi i powodująca sprzecz-
ności między nimi — nie było więc postrzegane jako aktywność krytyczna. Znacznie efektyw-
niejsze (i bardziej atrakcyjne) wydawało się podróżowanie uczestników między ośrodkami.
Generalnie, koszt formalizowania wiedzy i utrzymywania jej w aktualnym stanie jest zna-
czący. Może on być postrzegany jako inwestycja związana z:

• umożliwieniem walidacji wymagań i decyzji architektonicznych,


• umożliwieniem uczestnikom zmierzania ku spójnej koncepcji systemu,
• wspomaganiem uczestników podróżujących między wieloma ośrodkami.

Zwrot z tej inwestycji jest znikomy dla krótkich, nielicznych osobowo projektów; w ich
przypadku przeglądy partnerskie wydają się bardziej efektywnym sposobem transferowania
wiedzy.
764 Rozdział 16, ® Wszystko razem, czyli metodologie

Ile procesów cyklu życiowego?

Projekt ATRACT wzorowany był na cyklu życiowym ukierunkowanym na encje. FRIEND


i JAMES rozpoczęły swój żywot od kaskadowych iteracji i z czasem przeobraziły się w modele za-
rządzania ukierunkowane na encje, gdy klient zaakceptował pierwszy prototyp. Początkowa
iteracja obu tych projektów była konieczna dla wyszkolenia programistów nowicjuszy, lecz
w miarę postępów w realizacji oba te projekty przyjęły formę podobną do krótkich iteracji XP
i codziennego integrowania kodu. Wszystkie trzy projekty były wystarczająco małe, by możliwe
stało się utrzymywanie kameralnej atmosfery małych zespołów. Zauważmy, że w przypadku
projektu JAMES nie nastąpił transfer cyklu życiowego między ośrodkami: projekt, owszem,
spełnił swe zadanie i został uznany za pomyślnie zakończony, ale nie udało się utrzymać spójno-
ści w projekcie systemu. Stało się tak prawdopodobnie za sprawą ukierunkowania cyklu ży-
ciowego projektu na encje, nie na aktywności, i braku niezbędnej w takim przypadku ścisłej
współpracy programistów w ramach codziennego integrowania kodu.

Ile kontroli?

W typowym projekcie zakres kontroli zmienia się z czasem jego realizacji. W projektach
FRIEND i JAMES przez pierwsze dwa miesiące obowiązywała ścisła organizacja hierarchiczna,
umożliwiająca systematyczną rotację ról i zapewniająca duży zakres publikowanej informacji
na temat statusu projektu. Gdy rozpoczęło się kodowanie (mniej więcej w okolicy Święta
Dziękczynienia), studenci ze zdolnościami przywódczymi i umiejętnościami komunikacyjnymi
w naturalny sposób objęli role (odpowiednio) kierowników zespołów i łączników. Te spośród
praktykowanych dotąd procedur menedżerskich, które uznane zostały za pożyteczne (na przy-
kład protokołowanie zebrań), były utrzymywane przez studentów jako warte inwestowanego
w nie wysiłku. W naszej ocenie pozwalało to na rozluźnienie reguł i gdy studenci powrócili ze
świątecznej przerwy, dalsza realizacja projektu prowadzona była na sposób „zwinny", ukierun-
kowany na encje. Dyskusje o charakterze technicznym przeniosły się na fora poszczególnych
zespołów, także większość konfliktów rozstrzygana była na poziomie zespołów, zwykle w sposób
bardziej efektywny, niż miałoby to miejsce przy (hierarchicznym) udziale menedżera.
Innymi słowy, dwa pierwsze miesiące iteracji wykorzystane zostały na zbudowanie spój-
nej wizji wymagań i architektury i gdy ziarno to zostało posiane, należało pozwolić mu wykieł-
kować, rozluźniając reguły. Projekt zaczął wówczas ewoluować na wzór żywego organizmu,
wykorzystując naturalne mechanizmy selekcji i samoorganizacji. Struktura zespołu, ścieżki
komunikacyjne wyznaczane przez łączników i procedury zebrań przetrwały tę metamorfozę.
Nieustanna presja wynikająca z terminu testów akceptacyjnych (których nie można było prze-
sunąć) zapewniała pełne skoncentrowanie wszystkich zespołów na zmierzaniu do wspólnego
sukcesu. Tak więc, mimo iż kwalifikacje uczestników nie pozwalały początkowo na działanie
w warunkach samoorganizacji, jednak ta stała się możliwa dzięki początkowemu treningowi.
W przypadku systemu FRIEND nieograniczony dostęp do klienta zapewniał progra-
mistom spójny obraz dziedziny aplikacyjnej. Uczestnicy projektu JAMES mieli trochę gorzej
— rozproszenie projektu znacznie utrudniało spójną ewolucję w kierunku spełnienia kryteriów
akceptacyjnych klienta.
Generalnie „zwinne" organizacje spisują się znakomicie w sytuacji, gdy główni uczestnicy
współdzielą te same cele i tę samą koncepcję architektoniczną, dzięki prezentowaniu tej samej
wizji potrzeb klienckich lub nadzwyczaj klarownej koncepcji samego systemu. Organizacja hie-
16.5. Analizy przypadku 765

rarchiczna okazuje się natomiast niezbędna w skonfliktowanym środowisku, obywającym się


skąpą ilością zasobów.

Heurystyki metodologiczne
Bądź przygotowany na zmiany w systemie. Zmiany w projektach informatycznych są nieuniknione,
czy to ze strony klienta (nieprzewidziane problemy z użytecznością, nowe wymagania), czy też ze
strony programistów (nieprzewidziane problemy techniczne, nowe decyzje architektoniczne). Za-
mrożenie specyfikacji może wydawać się uzasadnione z perspektywy zachowania przewidywalności
projektu, lecz ostatecznie prowadzi do kiepskiego produktu niespełniającego oczekiwań klienta i użyt-
kowników. Zamiast więc wzbraniać się przed zmianami, zdefiniuj procesy mające na celu radzenie
sobie z nimi.

Uwzględnij możliwość zmiany organizacji projektu. Organizacja hierarchiczna i samoorganizacja


charakteryzują się zróżnicowanymi zaletami. Hierarchiczna organizacja daje bardziej przewidywane
rezultaty i najwłaściwsza jest w początkowych stadiach projektu. Samoorganizacja wnosi do projektu
powiew świeżości i może prowadzić do doskonałych wyników, jeśli jest odpowiednio ukierunkowana.
Gdy pogłębia się doświadczenie programistów w zakresie cyklu życiowego projektu i krystalizuje się
wśród nich coraz bardziej spójna wizja systemu, można pozostawić w gestii zespołów wiele procesów
decyzyjnych, zachowując jednak możliwość regularnego pomiaru postępu w realizacji projektu za
pomocą odpowiednio precyzyjnych metryk ilościowych i wskaźników jakości.

Uwzględnij możliwość zmiany procesów. Doświadczenie i wspólna wizja systemu ułatwiają przysto-
sowywanie procesów cyklu życiowego. Model cyklu życiowego może ewoluować od procesów ukie-
runkowanych na aktywności do procesów bazujących na encjach — w tym drugim przypadku
przydział pracy opiera się na zmianach wprowadzanych do „linii bazowych". Ponieważ podstawowe
wymagania są ustalone, projekt może być sterowany czasem (czyli emisjami w określonych terminach)
zamiast funkcjonalnością (czyli emisjami po zakończeniu implementowania poszczególnych cech).

Skracaj czas podejmowania decyzji zależnych od klienta. Poważne zmiany w systemie (nowa funkcjo-
nalność, nowy plan emisji) nie mogą odbywać się bez udziału klienta. Skracanie czasu podejmowania
takich krytycznych decyzji jest niezbędne dla zapobieżenia zboczeniu projektu w niewłaściwym
kierunku. Wymaga to efektywnego mechanizmu komunikacji z klientem i rozwiązywania problemów
z udziałem kompetentnych uczestników. W tym kontekście idealny okazuje się typ lokalnego, samo-
dzielnego klienta. Gdy brakuje takowego w projekcie, konieczne jest ustanowienie kanałów sprawnej
komunikacji między (oddalonym) klientem a uczestnikami projektu.

Buduj zaufanie. Uczestnicy oporni są pod względem komunikowania złych wiadomości, szczególnie
spowodowanych własnymi pomyłkami, w obawie przed spodziewanymi konsekwencjami. Tymcza-
sem wszelkie pomyłki i błędy są tym mniej kłopotliwe, im szybciej staną się ogólnie wiadome; nie-
znajomość ukrytych usterek może być zagrożeniem na miarę „być albo nie być" projektu. Musisz więc
z jednej strony, unikać stwarzania atmosfery uzasadniającej żywienie wspomnianych obaw, z drugiej
natomiast, zapewnić uczestnikom dostateczne zasoby do wykrywania i raportowania usterek.

Wyciągaj prawidłowe wnioski — i nic ponadto. Odkładanie podejmowania decyzji może być postrze-
gane jako krótkotrwały sukces, stanowiący podstawę do wyciągania nadmiernie optymistycznych
wniosków. Niekiedy samodzielny wysiłek może okazać się krytyczny na miarę dotrzymania (albo
zawalenia) terminu, jednakże skoncentrowanie istotnej wiedzy w grupie ograniczonej liczby uczest-
ników może stanowić dla projektu poważne zagrożenie. Przechodząc z reżimu poleceń i kontroli do
organizacji przywództwa i współpracy, przestajesz być tylko menedżerem, a stajesz się także nauczy-
cielem. Tak to już jest, że trzeba popełniać błędy, aby naprawę się czegoś nauczyć.
16.6. Literatura uzupełniająca

mana i B. Voddego [Larman / v o d d e , 2008] .

[Gleick, 1987].'D. Hock wj^owadzapojęcie „systemu chaordycznego" {chaordic system) w celu

1999]. H. Petroski zwraca uwagę na krytyczną rolę, jaką odgrywają błędy, pomyłki, awarie
i tym podobne w pomyślnym projektowaniu ([Petroski, 1994]). Uważa on, że nowatorskie
technologie są często reakcją na usterki istniejących produktów. Jegoksiążka pisana jest z per-

16.7. Ćwiczenia

ne? W jakiej k o l e j n o ś c i ^ ^ ' ^

czyli liczba uczestników, którzy opuścili projekt przed jego ukończeniem albo
dołączyli do projektu w trakcie jego realizacji. Wyobraź sobie, że zarządzasz projek-
tem, w którym wskaźnik ten jest wyjątkowo duży dla jednego z zespołów; spróbuj

16.5. Metodologia Royce'a definiuje sześć czynników projektowych — skalę, spójność


udziałowców, elastyczność procesów, dojrzałość procesów, ryzyko architektoniczne

do konkretnego projektu (patrz sekcja 16.4.1). Opisz w kategoriach tych czynników


Bibliografia 767

s t o p n i u . F l e m i n g zauważył kolistą p o w i e r z c h n i ę w o l n ą o d bakterii, w ś r o d k u tej


powierzchni znajdował się kawałek pleśni, który zanieczyścił kulturę bakterii. Za-
miast wyrzucić w s p o m n i a n ą płytkę — wszak eksperyment nie udał się z p o w o d u za-
nieczyszczeń — Fleming wyizolował pleśń, doprowadził d o jej rozrostu w roztworze
i odkrył w ten sposób substancję p o w s t r z y m u j ą c ą rozwój bakterii nawet w d u ż y m ,
800-krotnym rozcieńczeniu. Przedyskutuj odkrycie penicyliny, używając terminologii
i z a g a d n i e ń o p i s y w a n y c h w t y m rozdziale.
16.7. Opierając się n a założeniu o kulistym kształcie Ziemi, Krzysztof K o l u m b postanowił
znaleźć krótszą drogę d o Indii, płynąć na zachód zamiast na wschód, w efekcie za-
miast d o Indii, dopłynął do Ameryki. Przedyskutuj odkrycie Ameryki przez K o l u m -
ba, używając t e r m i n o l o g i i i z a g a d n i e ń o p i s y w a n y c h w t y m rozdziale.
16.8. Przypomnij sobie jakiś projekt, w którym brałeś udział. Używając kategorii m e t o d o -
logicznych zdefiniowanych w t y m rozdziale, opisz k o m p r o m i s y , jakie trzeba było
rozwiązywać w związku z realizacją tego p r o j e k t u .

Bibliografia
[AgileManifesto, 2001] http:/'/www. agilemanifesto. org.
[Aguanno, 2005] K. Aguanno Managing Agile Projects, Multi-Media Publications,
Ontario, 2005.
[Ambler, 2002] S. Ambler Agile Modeling: Effective Practices for Extreme Programming
and the Unified Process, John Wiley & Sons, 2002.
[Beck i Andres, 2005] K. Beck, C. Andres Extreme Programming Explained: Embrace Change,
wyd. drugie, Addison-Wesley, Reading, MA, 2005.
[Boehm i in., 2000] B. Boehm, E. Horowitz, R. Madachy, D. Reifer, B. K. Clark, B. Steece,
A. W. Brown, S. Chulani, C. Abts Software Cost Estimation with
COCOMOII, Prentice Hall, Upper Saddle River, NJ, 2000.
[Bonatti, 2001] W. Bonatti The Mountains of My Life, Modern Library, Random House,
New York, 2001.
[Buhl, 1956] H. Buhl Nanga Parbat Pilgrimage, Hodder & Stoughton, London, 1956.
[Cockburn, 2001] A. Cockburn Agile Software Development, Addison-Wesley, Reading,
MA, 2001.
[Cohn, 2006] M. Cohn Agile Estimating and Planning, Pearson, Upper Saddle River,
NJ, 2006.
[Cusumano i Selby, 1997] M. A. Cusumano, R. W. Selby How Microsoft Builds Software,
„Communications of the ACM", t. 40, nr 6, str. 53 - 61, 1997.
[Deming, 1982] E. W. Deming Out of the Crisis, MIT Press, Cambridge, MA, 1982.
wyd. drugie 2000.
[Fowler, 2005] M. Fowler The new methodology, http://www.martinfowler.com/
articles/newMethodology.html, zrewidowane w 2005.
[Gleick, 1987] J. Gleick Chaos — Making a New Science, Penguin Books Ltd,
Harmondsworth, Middlesex, 1987.
[Gladwin, 1964] T. Gladwin „Culture and logical process", w W. Goodenough (red.)
Explorations in Cultural Anthropology: Essays Presented to George
Peter Murdock, McGraw-Hill, New York, 1964.
768 Rozdział 16, ® Wszystko razem, czyli metodologie

[Grenning, 2002] J. Grenning Planning Poker, http://www.planningpoker.eom/y 2002.


[Highsmith i Orr, 2000] J. Highsmith, K. On Adaptive Software Development: A Collaborative
Approach to Managing Complex Systems, Dorset House, 2000.
[Highsmith, 2004] J. Highsmith Agile Project Management, Pearson Education, Boston, 2004.
[Hock, 1999] D. Hock Birth of the Chaordic Age, Berrett-Koehler Publishers, San
Francisco, 1999.
[Kelly, 1984] J. F. Kelly An iterative design methodology for user-friendly natural
language office information applications, „ACM Transactions on
Information Systems", t. 2, nr 1, styczeń 1984.
[Larman i Vodde, 2008] C. Larman, B. Vodde Scaling Lean & Agile Development: Thinking
and Organizational Tools for Large-Scale Scrum, Addison-Wesley,
Reading, MA, 2008.
[Marshall, 2001] R. Marshall „What really happened at K2?", rozdział 24. w [Bonatti, 2001].
[Norman, 2002] D. A. N o r m a n The Design of Everyday Things, Basic Books,
N e w York, 2002.
[Palmer i Felsing, 2002] S. Palmer, J. Felsing A Practical Guide to Feature-Driven Development,
Prentice Hall, Upper Saddle River, NJ, 2002.
[Paulkiin., 1995] M. C. Paulk, C. V. Weber, B. Curtis (red.) The Capability Maturity
Model: Guidelinesfor Improving the Software Process, Addison-Wesley,
Reading, MA, 1995.
[Petroski, 1992] H. Petroski To Engineer is Human, Vintage Books, Random House,
New York, 1992.
[Petroski, 1994] H. Petroski The Evolution of Useful Things, Vintage Books, Random
House, New York, 1994.
[Royce, 1998] W. Royce Software Project Management: A Unified Framework,
Addison-Wesley, Reading, MA, 1998.
[Rumbaugh i in., 1991] J. Rumbaugh, M. Błaha, W. Premerlani, F. Eddy, W. Lorensen Object-
Oriented Modeling and Design, Prentice Hall, Englewood Cliffs, NJ, 1991.
[Schlumberger] Schlumberger http://www.cyberflex.com/.
[Schwaber, 1995] K. Schwaber „Scrum Development Process", Business Object Design and
Implementation Workshop, OOPSLA'95, Austin, TX, październik 1995.
[Schwaber i Beedle, 2002] K. Schwaber, M. Beedle Agile Software Development with Scrum,
Prentice Hall, Upper Saddle River, NJ, 2002.
[Suchman, 2007] L. A. Suchman Human-Machine Reconfiguration: Plans and Situated
Actions, wyd. drugie Cambridge University Press, 2007.
[Takeuchi i Nonaka, 1986] H. Takeuchi, I. Nonaka The New New Product Development Game,
„Harward Business Review", 1986.
[Taylor, 1911] F. W. Taylor The Principles of Scientific Management, Harper Bros.,
New York, 1911.
[Tunde i Ray, 1994] A. O. Babatunde, W. H. Ray Process Dynamics, Modeling and Control
Oxford University Press, 1994.
[Weinberg, 1971] G. M. Weinberg The Psychology of Computer Programming, Van Nostrand,
New York, 1971.
Dodatki
Dodatek A

Wzorce projektowe

^ A ^ z o r c e projektowe stanowią częściowe rozwiązania często spotykanych problemów


projektowych i programistycznych, takich jak odseparowanie interfejsu od jego możli-
wych implementacji, przystosowanie przestarzałego kodu do nowego środowiska czy od-
separowanie klienta od zmian związanych ze specyficzną platformą. Wzorzec projektowy
składa się z kilku klas wzorcowych, które w połączeniu z dziedziczeniem i delegowaniem
dostarczają solidne i łatwe w modyfikacji rozwiązanie. Klasy te wymagają przystosowania
do specyfiki konkretnego systemu, niezmienna pozostaje jednak filozofia ich wykorzysty-
wania, objawiająca się specyficzną kombinacją dziedziczenia i delegowania. Koncepcje
dziedziczenia i delegowania nabierają na gruncie wzorców projektowych konkretnego,
praktycznego znaczenia — i to jest niejako dodatkowy płynący z nich pożytek o charakte-
rze dydaktycznym.
Od czasu, gdy ukazała się pierwsza książka na temat wzorców projektowych, napisana
przez E. Gammę, R. Heima, R. Johnsona i J. Vlissidesa [Gamma i in., 1994], pojawiło się wiele
innych propozycji wzorców projektowych, dedykowanych rozwiązywaniu problemów z zakresu
między innymi analizy (patrz prace M. Fowlera [Fowler, 1997] i C. Larmana [Larman, 2005]),
projektowania systemu (patrz praca F. Buschmanna, R. Meuniera, H. Rohnerta, P. Sommerlanda
i M. Stała [Buschmann i in., 1996]), middleware (patrz praca T. J. Mowbraya i R. C. Malveau
[Mowbray i Malveau, 1997]), modelowania procesów (patrz praca S. W. Amblera [Ambler,
1998]), zarządzania zależnościami (patrz praca P. Feilera i W. Tichego [Feiler i Tichy, 1998]),
zarządzania konfiguracją (patrz praca W. J. Browna, H. W. McCormicka i S. W. Thomasa
[Brown i in., 1999]) i adaptacją metodologii (patrz praca A. Elssamadisy'ego[Elssamadisy,
2009]). Sam termin „wzorzec projektowy" stał się chwytliwym hasłem i często bywa używany
(oraz nadużywany) w rozmaitych znaczeniach. W tym dodatku, pomyślanym jako katalog
referencyjny, ograniczymy się tylko do oryginalnego katalogu wzorców projektowych, definiują-
cego zwięzły zbiór eleganckich rozwiązań najczęstszych problemów. Każdy wzorzec opisujemy
krótko w języku naturalnym, przedstawiamy w postaci diagramu klas UML i opatrujemy heu-
rystykami pomocnymi w wyborze konkretnego wzorca. Odsyłamy także do konkretnych sekcji
tej książki, w których prezentowaliśmy zastosowania poszczególnych wzorców na konkretnych
przykładach. Zakładamy, że czytelnik posiada podstawową wiedzę z zakresu wzorców pro-
jektowych, koncepcji projektowania zorientowanego obiektowo i diagramów klas UML
772 Dodatek A • Wzorce projektowe

A.1. Fabryka abstrakcyjna (Abstract Factory)


— hermetyzacja platformy
Nazwa Wzorzec projektowy Fabryka abstrakcyjna.
Opis problemu Odseparowanie klienta od różnych platform dostarczających różnych
implementacji tego samego zbioru koncepcji.

Rozwiązanie Platforma (na przykład system okienkowy) reprezentowana jest jako zbiór
produktów abstrakcyjnych ( A b s t r a c t P r o d u c t ) , z których każdy reprezentuje
określoną koncepcję (przykładowo przycisk) realizowaną na każdej platformie.
W klasie AbstractFactory zdefiniowane są operacje tworzenia poszczególnych
produktów. Realizację konkretnej platformy stanowią: obiekt ConcreteFactory
i zbiór obiektów ConcreteProduct (po jednym obiekcie dla każdego produktu).
Obiekt C o n c r e t e F a c t o r y zależny jest tylko od „swego" zbioru produktów.
Klient (Cl i ent) zależny jest wyłącznie od klasy A b s t r a c t F a c t o r y , co umożliwia
wymianę platform w sposób dla niego niewidoczny.

Konsekwencje Klient oddzielony zostaje od klas reprezentujących konkretne produkty.


Możliwa jest dynamiczna zmiana rodzin produktów, także w czasie
wykonywania programu.
Dodawanie nowych produktów jest trudniejsze, wymaga bowiem
zrealizowania konkretnej fabryki (ConcreteFactory).

Przykłady Statyczna hermetyzacja niekompatybilnych infrastruktur dla inteligentnego


budynku (patrz sekcja 8.4.4).
Niezależność systemu ARENA od konkretnych gier (patrz sekcja 8.6.1).
Powiązane Dziedziczenie specyfikacyjne i dziedziczenie implementacyjne
koncepcje (patrz sekcja 8.3.2).
A.2. Adapter (Adapter) — otoczka dla starszego kodu 773

A.2. Adapter (Adapter) — otoczka dla starszego kodu


Nazwa Wzorzec projektowy Adapter.

Opis problemu Konwersja interfejsu przestarzałej klasy na inny interfejs oczekiwany przez obecnego
klienta, dzięki czemu klient będzie mógł współdziałać z przestarzałą klasą.
Rozwiązanie Klasa Adapter implementuje interfejs Cl i entlnterface oczekiwany przez
klienta (Cl i ent). Deleguje ona wywołania klienta do przestarzałej klasy
(LegacyCl ass), wykonując przy okazji niezbędne konwersje.

Konsekwencje Klient (Cl i ent) i przestarzała klasa (LegacyCl ass) współpracują ze sobą
bez potrzeby modyfikowania któregokolwiek z nich.
Adapter przydatny jest zarówno dla samej klasy LegacyCl ass, jak i dla
dowolnej jej subklasy.
Dla każdej specjalizacji interfejsu klienckiego (Cl i entlnterface)
wymagany jest nowy Adapter.
Przykłady Sortowanie instancji klasy Stri ng za pomocą metody sort () (patrz sekcja
8.4.2): klasa MyStri ngComparator jest adapterem wypełniającym lukę
między klasą Stri ng a interfejsem Comparator wykorzystywanym przez
metodę Array. sort ().
Powiązane Wzorzec projektowy Most (patrz sekcja A.3) wiążący interfejs z różnymi
koncepcje jego implementacjami.
774 Dodatek A • Wzorce projektowe

A.3. Most (Bridge) — podmiana implementacji


Nazwa Wzorzec projektowy Most.
Opis problemu Odseparowanie interfejsu od implementacji, dzięki czemu implementacje te
mogą być zmieniane w dowolnej chwili, także w czasie wykonywania programu.

Rozwiązanie W klasie Abstraction definiowany jest interfejs widoczny dla klienta (Cl i ent).
Implementor to abstrakcyjna klasa definiująca m e t o d y niższego p o z i o m u
d o s t ę p n e dla klasy Abs tract i on. Klasa Abstraction u t r z y m u j e referencję
do powiązanego z nią obiektu subklasy Implementor. Każda z klas Abstraction
i Implementor może być rozwijana niezależnie.

Konsekwencje Klient oddzielony zostaje od abstrakcyjnej i konkretnej implementacji.


Interfejsy i implementacje mogą by rozwijane niezależnie od siebie.
Przykłady Testowanie różnych implementacji tego samego interfejsu (patrz sekcja 8.4.1).
Powiązane Wzorzec projektowy (patrz sekcja A.2) wypełniający lukę między dwoma
koncepcje interfejsami.
A.4. Polecenie (Command) — hermetyzacja przepływu sterowania 775

A.4. Polecenie (Command) — hermetyzacja przepływu sterowania


Nazwa Wzorzec projektowy Polecenie.
Opis problemu Hermetyzacja żądań w celu umożliwienia ich wykonywania, wycofywania
i kolejkowania w ujednolicony sposób.
Rozwiązanie W klasie abstrakcyjnej Command deklarowany jest interfejs implementowany
przez każdą z klas reprezentujących konkretne polecenia (ConcreteCommand).
Każda z klas ConcreteCommand hermetyzuje usługi przeznaczone dla konkretnego
odbiorcy (Recei ver). Klient tworzy obiekty ConcreteCommand i wiąże je
z odpowiednimi obiektami Receiver. Klasa Invoker odpowiedzialna jest
za wykonywanie i wycofywanie poleceń.

Konsekwencje • Obiekt polecenia (Recei ver) oddzielony jest od algorytmu polecenia


(ConcreteCommand).
• Invoker oddzielony jest od konkretnego polecenia.
• Obiekty konkretnych poleceń (ConcreteCommand) mogą być tworzone,
niszczone i magazynowane.
• Nowe konkretne polecenia (obiekty ConcreteCommand) mogą być dodawane
bez zmiany istniejącego kodu.
Przykłady • Stos undo dla poleceń użytkownika: wszystkie dostępne dla użytkownika
polecenia są obiektami subklas wywodzących się z abstrakcyjnej superklasy
Command. Każda z tych subklas musi implementować m e t o d y do(), undo()
i redo(). Gdy polecenie zostaje w y k o n a n e , reprezentujący je obiekt
umieszczany jest na stosie undo. Gdy użytkownik chce anulować ostatnio
wydane polecenie, do obiektu znajdującego się na szczycie stosu wysłany
zostaje komunikat skutkujący wywołaniem metody undo ().
• Oddzielenie obiektów interfejsu od obiektów sterujących (patrz sekcja 8.4.5,
a także obiekty Act i on biblioteki Swi ng [JFC, 2009]). Wszystkie dostępne
dla użytkownika polecenia są obiektami subklas wywodzących się z abstrakcyjnej
superklasy Command. Obiekty interfejsu — opcje menu, przyciski i tym podobne
— wysyłają komunikaty do obiektów Command. Jedynie obiekty Command dokonują
m o d y f i k o w a n i a obiektów encji. Gdy zmienia się interfejs użytkownika
— na przykład pasek m e n u zostanie zastąpiony przez pasek narzędziowy
— modyfikowane są jedynie obiekty interfejsu.
Powiązane • Architektura M Y C (patrz rysunek 6.15).
koncepcje
776 Dodatek A • Wzorce projektowe

A.5. Kompozyt (Composite)


— rekurencyjna reprezentacja hierarchii
Nazwa Wzorzec projektowy Kompozyt.
Reprezentowanie hierarchii o zmiennej głębokości i szerokości, dzięki czemu
Opis problemu liście i węzły pośrednie (kompozyty) w drzewie hierarchii mogą być traktowane
w jednolity sposób.

Interfejs Component specyfikuje usługi współdzielone przez wszystkie liście


Rozwiązanie (Leaf) i węzły pośrednie (Composite), na przykład operację move(x, y)
powodującą przesunięcie elementu graficznego na wskazaną pozycję. Kompozyt
(Composi te) stanowi skojarzenie agregacyjne z k o m p o n e n t a m i (Component)
i realizuje daną usługę poprzez iterowanie po zawartych w nim
komponentach i wywoływanie tej usługi dla każdego z nich — przykładowo
wywołanie Compos i te.move(xs y) powoduje wywołanie metody
Component. move (x, y) dla każdego ze wspomnianych komponentów.
Konkretne działanie — na przykład zmiana współrzędnych obiektu graficznego
i odrysowanie go w nowej lokalizacji przez metodę move (x 9 y) — wykonywane
jest przez obiekty liści (Leaf).

Konsekwencje Klient (Cl i ent) wykorzystuje ten sam kod do operowania na liściach
i kompozytach.
Zachowanie specyficzne dla liści może być modyfikowane bez zmiany
hierarchii.
Dodawanie nowych klas liści może dokonywać się bez zmiany hierarchii.
Przykłady Wyświetlane elementy graficzne interfejsu mogą być organizowane w grupy,
obsługiwane (na przykład przesuwane) w sposób zintegrowany. Grupowanie
to może mieć charakter rekurencyjny — grupy mogą być elementami
większych grup.
W systemie plików katalogi mogą zawierać pliki lub inne katalogi (patrz
rysunek 2.23). Operacje przemianowywania, przesuwania i usuwania mogą
być takie same dla plików i katalogów.
Dekompozycja systemu na podsystemy (patrz rysunek 6.3): podsystem może
składać się z klas i innych podsystemów. Zauważmy jednak, że podsystemy
nie są implementowane jako kompozyty, do których dynamicznie dodawać
można nowe klasy.
Opis hierarchii zadań (patrz rysunek 6.8): każde zadanie może być utożsamiane
z kompozytem, podzadanie — z komponentem, a element działania — z liściem.
P o d o b n a zależność istnieje między fazami, aktywnościami i zadaniami
(patrz rysunek 15.6).
Powiązane Wzorzec projektowy Fasada (patrz sekcja A.6).
koncepcje
A.6. Fasada (Facade) — hermetyzacja podsystemów 111

A.6. Fasada (Facade) — hermetyzacja podsystemów


Nazwa Wzorzec projektowy Fasada.

Opis problemu Redukcja sprzężenia między zbiorem powiązanych klas a resztą systemu.

Rozwiązanie Pojedyncza klasa Facade implementuje wysokopoziomowy interfejs podsystemu,


przez wywoływanie metod klas na niższym poziomie. Klasa ta jest
nieprzezroczysta w tym sensie, że obiekt wywołujący metody interfejsu
nie m a bezpośredniego dostępu do wspomnianych klas niższego poziomu.
Rekurencyjne użycie wzorca projektowanego Fasada daje w rezultacie system
o architekturze warstwowej.

Konsekwencje • Odseparowanie klienta (Cl i e n t ) od niskopoziomowych klas podsystemu.


• Uproszczenie korzystania z podsystemu przez dostarczenie m e t o d
na wyższym poziomie.

Przykłady • Hermetyzacja podsystemu (patrz rysunek 6.28): kompilator (Compi 1 er)


składa się z analizatora leksykalnego, parsera (Parser), drzewa rozbioru
syntaktycznego (ParserTree), generatora kodu wynikowego (CodeGenerator)
i optymalizatora (Opt i mi zer). Użytkownik kompilujący kod źródłowy
m a jednak dostęp wyłącznie do kasy Compi 1 er, wywołującej odpowiednie
metody pozostałych klas.
Powiązane • Sprzężenie i spoistość (patrz sekcja 6.3.3), warstwy i partycje (patrz sekcja
koncepcje 6.3.4), wzorzec projektowy Kompozyt (patrz sekcja A.5).
778 Dodatek A • Wzorce projektowe

A.7. Obserwator (Observer) — oddzielenie encji od widoków


Nazwa Wzorzec projektowy Obserwator.

Opis problemu Utrzymywanie spójności między publikatorem a jego subskrybentami.

Rozwiązanie Publikator (Pub l isher), zwany także podmiotem (Subj ect— [Gamma i in., 1994]),
to obiekt, którego głównym zadaniem jest utrzymywanie pewnej informacji
o stanie — na przykład w formie macierzy. Jeden lub więcej subskrybentów
(Subscriber), zwanych także obserwatorami (Observer— [Gamma i in., 1994]),
wykorzystuje tę informację, na przykład do jej wyświetlania w formie wykresu.
Wprowadza to redundancję stanów publikatora i jego subskrybentów. W celu
rozwiązania tego problemu każdy z subskrybentów wywołuje metodę subscri be (),
powodującą zarejestrowanie u publikatora zapotrzebowania na powiadamianie
o każdej zmianie jego stanu. Każdy konkretny subskrybent (ConcreteSubscri ber)
definiuje także m e t o d ę update (), stanowiącą środek wspomnianego
powiadamiania — gdy publikator (Publisher) zmieni swój stan, wywołuje
swą metodę not i f y (), która z kolei powoduje wywołanie metody update ()
każdego zarejestrowanego subskrybenta.

Konsekwencje Odseparowanie publikatora od subskrybentów.


Możliwy intensywny przepływ komunikatów wynikający z częstych zmian
stanu publikatora.
Przykłady Interfejs Observer i klasa Observabl e języka Java, realizujące wzorzec
projektowy Obserwator [JFC, 2009].
Subskrypcja i powiadamianie w ramach architektury „model-widok-kontroler"
(MVC — patrz rysunek 6.15).
Oddzielenie stanu meczu od widoku meczów w systemie ARENA
(patrz sekcja 8.6.3).
Powiązane Obiekty encji, obiekty interfejsu (brzegowe) i obiekty sterujące
koncepcje (patrz sekcja 5.3.2).
A.8. Proxy (Proxy) — hermetyzacja kosztownych obiektów 779

A.8. Proxy (Proxy) — hermetyzacja kosztownych obiektów


Nazwa Wzorzec projektowy Proxy.

Opis problemu Poprawienie wydajności lub bezpieczeństwa systemu przez opóźnianie


kosztownych obliczeń, tworzenie obiektów tylko w razie rzeczywistej potrzeby,
weryfikację uprawnień dostępu do obiektu przed jego użyciem.

Rozwiązanie Obiekt-pełnomocnik (ProxyObj e c t ) działa w imieniu głównego obiektu


(Real Object). Obie klasy — ProxyObj ect i Real Object — implementują ten sam
interfejs, klasa ProxyObj e c t przechowuje część atrybutów klasy Real Object.
Obiekt-pełnomocnik realizuje w sposób kompletny niektóre żądania — na
przykład określenie rozmiarów obrazka — podczas gdy inne żądania delegowane
są do obiektu głównego. Obiekt-pełnomocnik jest odpowiedzialny za utworzenie
bądź załadowanie do pamięci obiektu głównego.

Konsekwencje • Dodatkowy poziom odwołania między klientem a obiektem.


• Klient oddzielony jest od ewentualnych zabiegów optymalizacji obiektu
głównego.

Przykłady • Ochrona przed nieuprawnionym dostępem (patrz rysunek 7.8) — klasa


skojarzeniowa Access definiuje zbiór operacji, za pomocą których Broker
uzyskuje dostęp do Portf ol i o. Dla każdej z tych operacji obiekt Portf ol i oProxy
wywołuje metodę i sAccessi bl e () w celu zweryfikowania, czy broker ten ma
prawo dostępu do konkretnego portfolio. Jeżeli uprawnienie dostępu zostanie
potwierdzone, obiekt Portfol i oProxy deleguje wywołanie metody do obiektu
Portfol i o, w przeciwnym razie obiekt nie zostaje załadowany do pamięci.
• Proxy m a g a z y n u j ą c e (patrz r y s u n e k 10.5) — obiekt ImageProxy działa
w imieniu obiektu Image przechowywanego na dysku. Obiekt ImageProxy
przechowuje tę samą informację, co obiekt Image (szerokość, wysokość,
położenie, rozdzielczość) z wyjątkiem samego obrazu. Obiekt ImageProxy
może więc realizować wszystkie żądania, które nie są bezpośrednio związane
z zawartością obrazu; tylko wtedy, gdy zawartość ta staje się istotna — na
przykład trzeba wyświetlić obraz na ekranie — obiekt ImageProxy tworzy
obiekt Real Image i ładuje z dysku rzeczywisty obraz.

Powiązane • Cache'owanie czasochłonnych obliczeń (patrz sekcja 10.4.1).


koncepcje
780 Dodatek A • Wzorce projektowe

A.9. Strategia (Strategy) — hermetyzacja algorytmów


Nazwa Wzorzec projektowy Strategia.
Opis problemu Rozdzielenie klasy wyznaczającej zasady od mechanizmów realizujących te
zasady, dzięki czemu mechanizmy te mogą być wymieniane w sposób niewidoczny
dla klienta.

Rozwiązanie Klient (Cl i ent) korzysta z usług dostępnych w danym kontekście (Context).
Każda z tych usług realizowana jest za p o m o c ą jednego z dostępnych
mechanizmów, wybranego przez obiekt Pol i cy. Abstrakcyjna klasa Strategy
opisuje interfejs wspólny dla wszystkich mechanizmów kontekstu. Konfiguracja
kontekstu jest zadaniem obiektu konkretnej strategii ( C o n c r e t e S t r a t e g y ) ,
tworzonego przez obiekt Pol i cy.

Konsekwencje Konkretne strategie ( C o n c r e t e S t r a t e g y ) mogą być wymieniane w sposób


niezauważalny dla kontekstu.
Obiekt Pol i cy decyduje o tym, która ze strategii jest najlepsza w danych
warunkach (na przykład przy rozstrzyganiu kompromisu między szybkością
a zajętością pamięci).
Nowe algorytmy mogą być dodawane bez modyfikowania klienta (Cl i e n t )
lub kontekstu (Context).
Przykłady Przełączanie połączeń sieciowych w aplikacjach mobilnych (patrz sekcja
8.4.3) — aplikacja posiada zdolność wykorzystywania różnorodnych
protokołów sieciowych (sieć LAN, sieć bezprzewodowa, UMTS, połączenie
dodzwaniane i tym podobne), zaś wybór konkretnego protokołu dokonywany
jest automatycznie na podstawie aktualnego kontekstu użytkownika
(lokalizacji, dostępności poszczególnych protokołów, kosztu, mocy sygnału
i tak dalej). Hermetyzacja wspomnianych protokołów za pomocą wzorca
projektowego Strategia umożliwia odseparowanie ich od wspólnego interfejsu
sieciowego.

Powiązane Wzorce projektowe Adapter (patrz sekcja A.2) i Most (patrz sekcja A.3).
koncepcje
A.10. Heurystyki pomocne w wyborze wzorców projektowych 781

A.10. Heurystyki pomocne w wyborze wzorców projektowych

Heurystyki wyboru wzorców projektowych w języku naturalnym


Wzorce projektowe ułatwiają realizowanie celów projektowych i wymagań pozafunkcyjnych.
Podobnie do heurystyk Abbotta opisywanych w rozdziale 5., kluczowe frazy języka potocznego
mogą sugerować odpowiednie kandydatury poszczególnych wzorców. W tabeli A.l prezentu-
jemy takie przykładowe frazy związane ze wzorcami opisywanymi w tym dodatku.

Tabela A . l . Frazy języka naturalnego sugerujące wybór poszczególnych wzorców projektowych

Prawdopodobny
Fraza
wzorzec projektowy
• „niezależność od dostawcy"
Fabryka abstrakcyjna
• „niezależność od platformy"

• „musi być zgodny z istniejącym interfejsem"


Adapter
• „musi współpracować z istniejącym k o m p o n e n t e m "

• „musi mieć możliwość rozszerzania o nowe protokoły" Most

• „wszystkie polecenia muszą mieć możliwość wycofywania"


Polecenie
• „wszystkie transakcje muszą być rejestrowane"

• „musi obsługiwać zagregowane struktury"


• „musi dopuszczać hierarchie o dowolnej głębokości i szerokości" Kompozyt

• „zasady muszą być oddzielone od realizujących je mechanizmów"


• „musi umożliwiać dynamiczną wymianę algorytmów podczas Strategia
wykonywania programu"
782 Dodatek A • Wzorce projektowe

Bibliografia
S. W. Ambler Process Patterns: Building Large-Scale Systems Using
[Ambler, 1998]
Object Technology, Cambridge University Press, New York, 1998.

W. J. Brown, H. W. McCormick, S. W. Thomas AntiPatterns and Patterns


[Brown i in., 1999]
in Software Configuration Management, Wiley, New York, 1999.

F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, M. Stal


[Buschmann i in., 1996]
Pattern-Oriented Software Architecture: A System of Patterns, Wiley,
Chichester, U.K., 1996.
[Elssamadisy, 2009] A. Elssamadisy Agile Adoption Patterns, Addison-Wesley, Reading,
MA, 2009.
[Feiler i Tichy, 1998] P. Feiler, W. Tichy „Propagator: A family of patterns", Proceedings
ofTOOLS-23'97, 28 lipca - 1 sierpnia 1997, Santa Barbara, CA.
[Fowler, 1997] M. Fowler Analysis Patterns: Reusable Object Models, Addison-Wesley,
Reading, MA, 1997.
[Gamma i in., 1994] E. G a m m a , R. Helm, R. Johnson, J. Vlissides Design Patterns:
Elements of Reusable Object-Oriented Software, Addison-Wesley,
Reading, MA, 1994. Wydanie polskie Wzorce projektowe. Elementy
oprogramowania obiektowego wielokrotnego użytku, Helion 2010.
[JFC, 2009] Java Foundation Classes, JDK Documentation, Javasoft, 2009.
[Larman, 2005] C. L a r m a n Applying UML and Patterns: An Introduction to
Object-Oriented Analysis and Design, wyd. trzecie, Prentice Hall,
Upper Saddle River, NJ, 2005.

[Mowbray i Malveau, 1997] T. J. M o w b r a y , R. C. Malveau CORBA Design Patterns, Wiley,


N e w York, 1997.
DODATEK B

Objaśnienia haseł

B.1. Terminologia
Abstrakcja (abstraction) — Klasyfikacja zjawiska w kategoriach koncepcji. Patrz także
modelowanie.
Abstrakcyjny typ danych (abstract data type — ADT) — Typ danych definiowany w specyfi-
kacji niezależnej od konkretnej implementacji.
ACL — Patrz lista kontroli dostępu.
Adaptowalność (adaptability) — Zdolność systemu do radzenia sobie z dodatkowymi
koncepcjami dziedziny aplikacyjnej.
ADT — Patrz abstrakcyjny typ danych.
Agenda zebrania (meeting agenda) — Dokument związany z przygotowaniem i prowadze-
niem zebrania. Zawiera co najmniej trzy części:
• nagłówek określający czas i miejsce zebrania oraz jego uczestników,
• listę tematów zgłoszonych przez uczestników,
• listę zagadnień do przedyskutowania i rozstrzygnięcia.
Agregacja (aggregation) — Skojarzenie ukazujące relację „część-całość" między klasami.
Przykładem agregacji jest system składający się z podsystemów.
Agregacja kompozycyjna (composition aggregation) — Relacja agregacji, w ramach której
istnienie części uwarunkowane jest istnieniem całości. Przeciwieństwo agregacji współ-
dzielonej.
Agregacja współdzielona (shared aggregation) — Relacja agregacji, w ramach której część
może istnieć niezależnie od całości. Przeciwieństwo agregacji kompozycyjnej.
Agregat CM (CM aggregate) — To samo, co agregat zarządzania konfiguracją.
Agregat zarządzania k o n f i g u r a c j ą (configuration management aggregate — C M aggregate)
— Agregat powiązanych elementów konfiguracji.
784 Dodatek B • Objaśnienia haseł

Akcja (action) — Podstawowa jednostka zachowania. Akcja może wykorzystywać informację


wejściową, produkować informację wyjściową i zmieniać stan systemu. Akcja jest jednostką
niepodzielną w tym sensie, że nie jest możliwa jej dekompozycja. Akcje mogą być kojarzone
ze specyficznymi punktami maszyny stanów — wejściami do stanów, wyjściami ze stanów
i przejściami między stanami. Akcje trwają zwykle bardzo krótko i nie mogą być przerywane.
Aktor (actor) — Zewnętrzna encja przejawiająca potrzebę wymiany informacji z systemem.
Encja ta może być utożsamiana z rolą lub innym systemem.
Aktor uczestniczący (participating actor) — Aktor przejawiający interakcję z przypadkiem
użycia.
Aktywność (activity) — 1. W cyklu życiowym oprogramowania — zbiór zadań wykonywa-
nych w związku z dążeniem do określonego celu. Aktywności mogą obejmować różne liczby
zadań, zależnie od celu, jakiemu są podporządkowane. Przykładami aktywności są: zbieranie
wymagań, identyfikowanie obiektów i testowanie jednostkowe. Patrz także Proces. 2. Na dia-
gramie stanów — zachowanie prezentowane tak długo, jak długo obiekt znajduje się w okre-
ślonym stanie. Należy odróżnić aktywność od akcji: podczas gdy akcje trwają krótko i są
nieprzerywalne, aktywności mogą zajmować dużo czasu i kończą się w momencie, gdy obiekt
wyjdzie z aktualnego stanu.
Analityczny model obiektowy (analysis object model) — Obiektowa część modelu anali-
tycznego.
Analityk (analyst) — Rola, której zadaniem jest pozyskiwanie, od użytkowników i klientów,
informacji z zakresu dziedziny aplikacyjnej oraz konstruowanie na tej podstawie modelu
przypadków użycia.
Analiza (analysis) — Aktywność, którą realizują programiści w celu upewnienia się, że
wymagania stawiane systemowi są poprawne, kompletne, spójne i jednoznaczne.
Analiza zorientowana obiektowo (object-oriented analysis) — Aktywność skoncentrowana
na modelowaniu dziedziny aplikacyjnej za pomocą obiektów.
API — Patrz interfejs programisty aplikacji.
Architekt (architect) — Rola programistyczna związana z podejmowaniem strategicznych
decyzji dotyczących systemu i konstruowaniem modelu projektu systemu.
Architekt systemu (system architect) — Patrz architekt.
Architektura (architecture) — Patrz model projektu systemu.
Architektura czterowarstwowa (four-tier architectural style) — Styl architektoniczny organi-
zujący system w postaci czterech warstw: klienta prezentacji, serwer prezentacji, logikę
aplikacji i magazynowanie danych.
Architektura oprogramowania (software architecture) — Patrz model projektu systemu.
Architektura otwarta (open architecture) — Architektura warstwowa, w ramach której
każda warstwa może być uzależniona w dowolny sposób od warstw niższych. Przeciwieństwo
architektury zamkniętej.
Architektura repozytoryjna (repository architectural style) — Styl architektoniczny, zgod-
nie z którym trwałe dane są zarządzane i przechowywane przez pojedynczy podsystem. Pod-
system ten stanowi jednocześnie wyłączne medium komunikowania się pozostałych „peryfe-
ryjnych" podsystemów.
B.1. Terminologia 785

Architektura trójwarstwowa (three-tier architectural style) — Styl architektoniczny organi-


zujący system w postaci trzech warstw: interfejsu, logiki aplikacji i magazynowania da-
nych.
Architektura zamknięta (closed architecture) — Architektura warstwowa, w ramach której
każda warstwa (z wyjątkiem najniższej) zależna jest wyłącznie od warstwy bezpośrednio
niższej. Przeciwieństwo architektury otwartej.
Argument (argument) — Wartość przekazywana w ramach komunikatu.
Argumentacja (argumentation) — Debata uczestników zmierzająca do rozstrzygnięcia
zagadnienia.
Atrybut (attribute) — Nazwana właściwość klasy, definiująca zakres wartości, jakie może
zawierać obiekt tej klasy. Przykładowo właściwość time jest atrybutem klasy Watch.
Audyt (auditing) — Walidacja wersji przed jej wyemitowaniem, w celu upewnienia się co
do jej spójności i kompletności.
Audytor (auditor) — Rola odpowiedzialna za wybieranie promocji i ocenę ich przydatności
do emitowania.
Awaria (failure) — Sytuacja, w której zachowanie przejawiane przez system różni się od
oczekiwanego.
Bezpieczeństwo (safety) — Miara nieobecności katastroficznych konsekwencji dla środowiska.
Biblioteka kontrolowana — Patrz katalog główny.
Biblioteka oprogramowania (software library) — Magazyn wersji oprogramowania, z moż-
liwościami śledzenia statusu zmian między nimi.
Błąd (error) — Patrz błędny stan.
Błędny stan (erroneous state) — Stan systemu, w którym kontynuowanie jego pracy prowadzi
do wystąpienia awarii.
Bodziec (stimulus) — Komunikat wysłany do obiektu lub systemu przez aktora lub inny
obiekt, skutkujący zazwyczaj wykonaniem konkretnej operacji. Przykładami bodźców są:
kliknięcie przycisku interfejsu, wybranie opcji menu, wpisanie polecenia lub przesłanie
pakietu przez sieć.
Burza mózgów (brainstorming) — Planowe zdarzenie komunikacyjne, w ramach którego
uczestnicy generują dużą liczbę różnych ewentualności rozstrzygnięcia zagadnienia.
Cel (goal) — Wysokopoziomowa zasada wykorzystywana do prowadzenia projektu. Cele
definiują istotne atrybuty systemu, na przykład bezpieczeństwo samochodu, niski koszt
gotowych komponentów.
Cel projektowy (design goal) — Jakościowy aspekt systemu podlegający optymalizowaniu.
Najczęściej wypływający z wymagań pozafunkcyjnych, stanowiący podstawę podejmowania
decyzji. Przykładami celów projektowych są użyteczność, niezawodność i bezpieczeństwo.
Chronometrażysta (timekeeper) — Rola odpowiedzialna za kontrolowanie przebiegu cza-
sowego zebrania; dzięki tej kontroli główny facilitator może przyspieszać rozstrzyganie
niektórych zagadnień, jeśli jest to konieczne.
Ciąg OCL (OCL sequence) — Kolekcja w języku O C L reprezentująca listę u p o r z ą d k o w a n ą .
Ciągi wykorzystywane są w języku O C L d o r e p r e z e n t o w a n i a w y n i k ó w nawigacji p o poje-

CRC — Patrz karta CRC.

Cyk! życiowy ( l i f e cycle) - Patrz cykl życiowy oprogramowania.


Cykl życiowy oprogramowania (software life cycle) — Wszystkie aktywności i p r o d u k t y

Czas o d p o w i e d z i (response time) — A t r y b u t systemu wyrażający szybkość jego reakcji n a


w p r o w a d z e n i e danych przez użytkownika.
Czas przestoju (slack time) — Dla danego zadania — maksymalny czas, o jaki m o ż e opóźnić
się realizacja zadania bez konsekwencji w postaci o p ó ź n i e ń projektu.
Członek projektu (project member) — T o samo, co uczestnik.
Czytelność (readability) — W ł a s n o ś ć systemu oznaczająca łatwość jego zrozumienia n a
podstawie analizy k o d u źródłowego.
D e b u g o w a n i e (debugging) — Technika wykrywania usterki będącej przyczyną niespo-
dziewanej awarii, drogą k o n t r o l o w a n e g o przechodzenia przez kolejne stany systemu.

p r e z e n t o w a n i e e l e m e n t ó w ilościowych w p o d e j m o w a n i u decyzji.
Decyzja (decision) — 1. W kontekście d i a g r a m u aktywności — rozgałęzienie przepływu
sterowania. Decyzja oznacza wybór j e d n e j ze ścieżek przepływu n a podstawie określonego
w a r u n k u . 2. W kontekście m o d e l o w a n i a zagadnień — rozstrzygnięcie zagadnienia.
Decyzja implementacyj na (implementation decision) — Decyzja projektowa nie wpływaj ąca
na interfejs klasy lub p o d s y s t e m u .
D e f e k t (defect) - Patrz usterka.
Definiowanie problemu (problem definition) — Planowe zdarzenie komunikacyjne, w ra-
m a c h k t ó r e g o klient i m e n e d ż e r p r o j e k t u definiują zakres f u n k c j o n a l n y systemu.
Deklaracja p r o b l e m u (problem statement) — D o k u m e n t s p o r z ą d z o n y wspólnie przez
klienta i menedżera projektu, opisujący skrótowo zakres systemu, między innymi wymagania
w y s o k o p o z i o m o w e , środowisko docelowe, p r o d u k t y finalne i kryteria akceptacyjne. Patrz
także u m o w a projektu.
D e k o m p o z y c j a hierarchiczna (hierarchical decomposition) — D e k o m p o z y c j a systemu,
której w y n i k i e m jest u p o r z ą d k o w a n y zbiór warstw.

Każdy p o d s y s t e m opisywany jest w kategoriach świadczonych przez niego usług (na etapie
projektowania systemu) i w formie API (na etapie projektowania obiektów). Dekompozycja
systemu n a p o d s y s t e m y jest częścią modelu projektu systemu.
B.1. Terminologia 787

Delegowanie (delegation) — Mechanizm wielokrotnego wykorzystywania kodu polegający


na przekazywaniu (delegowaniu) komunikatów do innej klasy w celu uzyskania pożądanego
zachowania.
Delta (delta) — Zbiór różnic między dwiema kolejnymi wersjami, zwana także zmianą.
Diagram aktywności (activity diagram) — Notacja UML reprezentująca zachowanie systemu
w kategoriach aktywności. Aktywność jest stanem reprezentującym wykonywanie zbioru
operacji. Zakończenie wykonywania wszystkich aktywności wchodzących w skład tego
zbioru wyzwala przejście obiektu do nowego stanu. Diagramy aktywności wykorzystywane są
do ilustrowania kombinacji przepływu sterowania i przepływu danych.
Diagram interakcji (interaction diagram) — Notacja UML reprezentująca zachowanie systemu
w kategoriach interakcji między obiektami. Diagramy interakcji wykorzystywane są do identyfi-
kowania obiektów dziedziny aplikacyjnej i dziedziny realizacyjnej. Diagramy interakcji stano-
wią uogólniony środek odwoływania się do diagramów komunikacji i diagramów sekwencji.
Diagram klas (class diagram) — Notacja UML reprezentująca strukturę systemu w katego-
riach obiektów, klas, atrybutów, operacji i skojarzeń. Diagramy klas wykorzystywane są do
przedstawiania modeli obiektowych tworzonego systemu.
Diagram komunikacyjny (communication diagram) -— Notacja UML reprezentująca za-
chowanie systemu w kategoriach szeregu interakcji między grupami obiektów. Każdy obiekt
przedstawiany jest w formie prostokąta, zaś informacje wymieniane między obiektami
symbolizowane są przez łączące te obiekty linie, etykietowane nazwami przesyłanych ko-
munikatów. Kolejność występowania poszczególnych interakcji oznaczana jest przez liczby
naturalne poprzedzające nazwy odnośnych komunikatów. Patrz także diagram sekwencji.
Diagram maszyny stanowej — To samo, co diagram stanów.
Diagram obiektów (object diagram) — Diagram klas UML, przedstawiający jedynie instancje
tych klas.
Diagram przypadków użycia (use case diagram) — Notacja UML, wykorzystywana na etapie
zbierania i analizowania wymagań do reprezentowania funkcjonalności systemu. Przypadek
użycia opisuje funkcję systemu w kategoriach sekwencji interakcji między aktorem a syste-
mem. Z przypadkiem użycia mogą być także związane warunki wejściowe (ich spełnienia
wymaga się przed zainicjowaniem przypadku użycia) oraz warunki wyjściowe (ich spełnienie
gwarantowane jest po zakończeniu przypadku użycia).
Diagram sekwencji (sequence diagram) — Notacja UML przedstawiająca zachowanie systemu
jako ciąg interakcji między obiektami. Każdemu obiektowi odpowiada jedna kolumna dia-
gramu, każdej interakcji — strzałka między kolumnami. Diagramy sekwencji wykorzystywane
są na etapie analizy wymagań w celu wykrycia brakujących obiektów, atrybutów i relacji, jak
również na etapie projektowania obiektów w celu doskonalenia specyfikacji klas. Patrz
także diagram komunikacyjny.
Diagram stanów (state machine diagram) — Notacja UML reprezentująca zachowanie
konkretnego obiektu w kategoriach zbioru stanów i przejść między tymi stanami. Stan jest
reprezentacją konkretnego zbioru wartości przechowywanych przez obiekt. Przejście między
stanami określa przyszły stan obiektu w kontekście stanu bieżącego i określonego warunku.
Diagramy stanów wykorzystywane są na etapie analizy do opisywania zachowania obiektów
zależnego od konkretnego stanu.
788 Dodatek B • Objaśnienia haseł

Diagram wdrażania (deployment diagram) — Diagram UML reprezentujący komponenty


wykonywalne i ich przyporządkowanie węzłom sprzętowym.
Dokładność (accuracy) — Ilościowa miara wielkości błędu.
Dokument analizy wymagań (Requirements Analysis Document — RAD) — Dokument
opisujący model analityczny.
Dokument projektu obiektów (Object Design Document — ODD) — Dokument opisujący
model projektu obiektów. Dokument ten jest często generowany na podstawie komentarzy
wbudowanych w kod źródłowy.
Dokument projektu systemu (System Design Document — SDD) — Dokument opisujący
model projektu systemu.
Dokumentalista (technical writer) — Patrz redaktor dokumentu.
Dostępność (availability) — Stopień operatywności systemu lub komponentu.
DRL — Patrz Decision Representation Language.
Drugi facilitator (secondary facilitator) — Patrz facilitator.
Dynamiczna kontrola dostępu (dynamie access control) — Polityka dostępu, której zasady
definiowane są w trakcie wykonywania programu. Przeciwieństwo statycznej kontroli dostępu.
Dziedziczenie (inheritance) — Technika wielokrotnego wykorzystywania kodu, zgodnie z którą
klasa pochodna dziedziczy wszystkie atrybuty i operacje po swej klasie macierzystej. Dziedzi-
czenie wykorzystywane jest do realizacji relacji dziedziczenia.
Dziedziczenie implementacyjne (implementation inheritance) — Dziedziczenie stosowane
wyłącznie w celu powtórnego wykorzystania fragmentu kodu. Jest niezgodne z zasadą
zastępowania Liskov.
Dziedziczenie interfejsu (interface inheritance) — To samo, co dziedziczenie specyfikacyjne.
Dziedziczenie kontraktu (contract inheritance) — Dziedziczenie przez subklasę kontraktu
obowiązującego dla jej superklasy.
Dziedziczenie specyfikacyjne (specification inheritance) — Dziedziczenie wykorzystywane
jako środek podtypowania.
Dziedzina aplikacyjna (application domain) — Reprezentacja wszystkich aspektów problemu
użytkownika, między innymi fizycznego środowiska, w którym działać ma docelowy system,
jego użytkowników i realizowanych przez nich procesów. Na etapie zbierania wymagań
i analizy dziedzina aplikacyjna reprezentowana jest przez model analityczny. Dziedzina
aplikacyjna nazywana jest także dziedziną problemową.
Dziedzina implementacyjna (implementation domain) — To samo, co dziedzina realizacyjna.
Dziedzina problemowa (problem domain) — To samo, co dziedzina aplikacyjna.
Dziedzina realizacyjna (solution domain) — Przestrzeń wszystkich możliwych systemów
w kontekście ich projektowania, specyfikowania ich obiektów i implementowania. Przeci-
wieństwo dziedziny realizacyjnej.
Ekspert komponentu (component expert) — Rola programistyczna odpowiedzialna za do-
starczanie szczegółowej wiedzy na temat określonego komponentu.
B.1. Terminologia 789

Ekspert wzorca (pattern expert) — Rola programistyczna, odpowiedzialna za dostarczanie


szczegółowej wiedzy na temat konkretnego wzorca projektowego.
Ekstender klasy (class extender) — Programista realizujący specjalizację danej klasy. Mimo iż,
podobnie jak użytkownik klasy, może on korzystać z operacji definiowanych przez klasę,
to jednak koncentruje się głównie na specjalizowanej wersji oferowanych przez nią usług.
Elaboration — Patrz jednolity proces wytwarzania oprogramowania.
Element działania (action item) — Zadanie przypisane uczestnikowi, wraz z terminem
wykonania, zwykle w wyniku rozstrzygnięcia zagadnienia.
Element konfiguracji (configuration item) — Produkt traktowany jako pojedyncza encja
z perspektywy zarządzania konfiguracją, podlegający kontroli jako linia bazowa.
Emisja (release) — 1. W kontekście zarządzania konfiguracją — wersja produktu przeznaczona
do udostępnienia klientowi lub użytkownikom. Patrz także promocja. 2. Planowe zdarzenie
komunikacyjne, polegające na powiadomieniu uczestników projektu o pojawieniu się nowej
emisji (w znaczeniu punktu 1.).
Ewentualność (alternative) — To samo, co propozycja.
Facilitator (facilitator) — Rola menedżerska, odpowiedzialna za organizowanie i prowa-
dzenie zebrań. Pierwszy (główny) facilitator sporządza agendę zebrania, powiadamia zainte-
resowanych uczestników i nadzoruje zgodność przebiegu zebrania z agendą. Drugi facilitator
wspomaga facilitatora głównego w utrzymywaniu zebrania z ryzach wyznaczonych przez
agendę.
Falsyfikacja (falsification) — Proces jawnego dążenia do obnażenia usterek tkwiących
w modelu, na przykład pominięcia istotnych szczegółów lub reprezentowania ich w sposób
nieprawidłowy. Przykładami falsyfikacji są prototypowanie i testowanie.
Faza (phase) — Często używany synonim aktywności lub procesu.
Faza definicyjna projektu (project definition phase) — Faza, w której potencjalny klient,
menedżer projektu i architekt oprogramowania uzgadniają wstępną deklarację problemu,
harmonogram i architekturę oprogramowania.
Faza koncepcyjna projektu (project conception phase) — Pierwsza faza projektu, w której
rodzi się jego idea. W przypadku projektu programistycznego idea ta jest zwykle następ-
stwem potrzeb rynku lub dostępności nowych technologii.
Faza startowa projektu (project start phase) — Faza rozpoczynająca się w momencie, gdy
do projektu dołączają klient i menedżer. W trakcie tej fazy menedżer projektu definiuje jego
infrastrukturę, zatrudnia uczestników, organizuje ich w zespoły, definiuje główne „kamienie
milowe" i oficjalnie uruchamia realizację projektu.
Faza ustalona projektu (project steady state) — Faza, w której menedżer projektu monitoruje
i kontroluje postęp prac w projekcie.
Filtry i potoki (pipe and filter architectural style) — Styl architektoniczny, zgodnie z którym
podsystemy, zwane filtrami, połączone są w ciąg za pomocą potoków, służących do prze-
kazywania danych między kolejnymi filtrami. Poszczególne filtry są od siebie niezależne,
mogą więc być łączone w dowolnej kolejności (i w dowolnych zestawach).
Firewall (firewall) — To samo, co zapora sieciowa.
790 Dodatek B • Objaśnienia haseł

Framework (framework) — Zbiór klas dostarczający ogólne rozwiązanie, które może być
przystosowywane do konkretnego zastosowania lub konkretnego podsystemu.
Framework aplikacyjny (application framework) — Wykorzystywana w sposób powta-
rzalny częściowa aplikacja, przez specjalizacje której konstruuje się konkretne aplikacje.
Patrz także biblioteka klas.
Framework aplikacyjny (enterprise application framework) — Framework przystosowany
do specyfiki aplikacji biznesowych.
Framework białoskrzynkowy (whitebox framework) — Framework bazujący na dziedzi-
czeniu i wiązaniu dynamicznym jako środkach rozszerzalności. Przeciwieństwo frame-
worku czarnoskrzynkowego.
Framework czarnoskrzynkowy (blackbox framework) — Framework, którego rozszerzal-
ność opiera się na dobrze zdefiniowanych interfejsach. Przeciwieństwo frameworku bia-
łoskrzynkowego.
Framework infrastrukturalny (infrastructure framework) — Framework wykorzystywany do
realizacji podsystemu infrastruktury, na przykład interfejsu użytkownika lub podsystemu
magazynowania danych.
Framework pośredniczący (middleware framework) — Framework wykorzystywany do
integrowania rozproszonych aplikacji i komponentów.
Funkcje projektowe (project functions) — Aktywności realizowane przez cały czas trwania
projektu.
Gałąź (branch) — W zarządzaniu konfiguracją równoległa ścieżka rozwojowa.
Generalizacja (generalization) — Aktywność modelowania skutkująca identyfikowaniem
abstrakcyjnych koncepcji na podstawie koncepcji niskopoziomowych.
Globalna tabela dostępu (global access table) — Reprezentacja macierzy kontroli dostępu
w formie zbioru krotek o postaci (aktor, klasa, operacja). Każda z tych krotek odzwierciedla
pojedyncze uprawnienie dostępu, odpowiada zatem pojedynczej komórce macierzy dostępu.
Główny facilitator (primary facilitator) — Patrz facilitator.
Graniczny przypadek użycia (boundary use case) — Przypadek użycia związany z warun-
kiem granicznym.
Groupware — Każde narzędzie programistyczne wspierające wymianę informacji w kręgu
uczestników. Rozproszone, jednoczesne groupware jest środkiem komunikacji synchronicz-
nej, natomiast rozproszone, niejednoczesne groupware wspiera komunikację asynchroniczną.
Grupa procesów (process group) — Zbiór powiązanych procesów. Przykładowe grupy
procesów to procesy menedżerskie, procesy prerealizacyjne i procesy postrealizacyjne.
Guillemets — Poziome znaki cytowania, ograniczające stereotyp języka UML.
Harmonogram (schedule) — Odwzorowanie modelu zadań w upływający czas, w kategoriach
czasu kalendarzowego.
IBIS — Patrz Issue-Based Information System.
B.1. Terminologia 791

Idealny tydzień (ideal week) — W metodologii programowania ekstremalnego tydzień


roboczy, w którym cały wysiłek programistów dedykowany jest implementowaniu historii
użytkownika. Patrz także szybkość projektu.
Identyfikator wersji (version identifier) — Liczba, ciąg liczb lub nazwa jednoznacznie
identyfikująca daną wersję.
Identyfikowalność (traceability) — Własność modelu oznaczająca możliwość kojarzenia
elementu modelu z oryginalnym wymaganiem lub racjonalizacją, uzasadniającymi jego
istnienie.
Implementator (implementor) — Patrz implementator klasy.
Implementator klasy (class implementor) — Programista realizujący implementację danej
klasy, odpowiedzialny za zaprojektowanie wewnętrznych struktur danych klasy oraz kodu
wszystkich jej operacji.
Implementowanie (implementation) — Aktywność polegająca na transformowaniu modelu
obiektów na kod programu.
Inception — Patrz jednolity proces wytwarzania oprogramowania.
Inicjacja projektu (project initiation) — Według standardu I E E E 1074 — aktywność
menedżerska, polegająca na definiowaniu zakresu projektu i zasobów niezbędnych do jego
realizacji. W tej książce inicjacja projektu nazywana jest fazą startową.
Inspekcja (inspection) — Planowe zdarzenie komunikacyjne, w ramach którego programi-
ści wspólnie dokonują formalnego przeglądu produktu pod kątem listy predefiniowanych
kryteriów. Patrz także wędrówka po kodzie.
Inspekcja komponentu (component inspection) — Technika testowania polegająca na wy-
szukiwaniu usterek w kodzie komponentu w drodze jego „ręcznej" inspekcji.
Inspekcja problemu (problem inspection) — Planowe zdarzenie komunikacyjne polegające
na pozyskiwaniu przez programistę wiedzy z dziedziny aplikacyjnej, przez identyfikowanie
potrzeb klienta i użytkowników oraz przez analizę deklaracji problemu.
Instalowanie (installation) — Aktywność polegająca na instalacji i testowaniu systemu w jego
docelowym środowisku. Instalowanie systemu często łączone jest ze szkoleniem jego użyt-
kowników.
Instancja (instance) — Egzemplarz typu danych. Przykładowo 1291 to instancja typu i nt,
natomiast 3.14 to instancja typu f l o a t .
Instancja aktora uczestniczącego (participating actor instance) — Instancja aktora prze-
jawiającego interakcję ze scenariuszem.
Interfejs podsystemu (subsystem interface) — Zbiór operacji podsystemu, dostępnych
publicznie dla innych podsystemów.
Interfejs programisty aplikacji (Application Programmer Interface — API) — Kompletny
zbiór szczegółowo zdefiniowanych operacji udostępnianych przez podsystem.
Interfejs wzorca (pattern interface) — Część wzorca projektowego widoczna dla klienta.
Często występująca pod postacią klasy abstrakcyjnej lub interfejsu abstrakcyjnego.
792 Dodatek B • Objaśnienia haseł

Inżynier API (API engineer) — Rola koncentrująca się na definiowaniu API dla podsystemu.
Rola ta jest często łączona z rolą łącznika architektonicznego.
Inżynieria dziewicza — To samo, co inżynieria pierwotna.
Inżynieria interfejsu (interface engineering) — Projekt, którego celem jest ponowne za-
projektowanie lub ponowne zaimplementowanie interfejsu systemu, bez naruszania jego
podstawowej funkcjonalności. Patrz także inżynieria pierwotna, inżynieria wtórna.
Inżynieria odwracająca (reverse engineering) — Proces zastosowania transformacji do
zbioru elementów kodu źródłowego, dający w rezultacie odpowiedni zbiór elementów
modelu. Celem inżynierii odwracającej jest odtworzenie modelu istniejącego systemu, który
to model został zagubiony, stracił aktualność lub po prostu nigdy nie istniał. Przeciwieństwo
inżynierii postępującej.
Inżynieria pierwotna (greenfield engineering) — Tworzenie systemu „od zera". Patrz także
inżynieria wtórna, inżynieria interfejsu.
Inżynieria postępująca (forward engineering) — Proces zastosowania transformacji do
elementów modelu, dający w rezultacie odpowiedni fragment kodu źródłowego — deklaracje
klasy, wyrażenie lub schemat bazy danych. Przeciwieństwo inżynierii odwracającej.
Inżynieria wahadłowa (round-trip engineering) — Aktywność pielęgnowania modelu,
stanowiąca kombinację inżynierii postępującej i inżynierii odwracającej. Zmiany w imple-
mentacji propagowane są do modelu analitycznego i modelu projektu systemu w ramach
inżynierii odwracającej, zaś zmiany we wspomnianych modelach odzwierciedlane są w kodzie
źródłowym za pomocą inżynierii postępującej.
Inżynieria wtórna (reengineering) — Projekt, którego celem jest ponowne zaprojektowanie
lub ponowne zaimplementowanie systemu i towarzyszących mu procesów biznesowych.
Patrz także inżynieria pierwotna, inżynieria interfejsu.
Inżynieria wymagań (requirements engineering) — Aktywność obejmująca zbieranie
wymagań i ich analizę.
Issue-Based Information System — IBIS (Issue-Based Information System — IBIS) — Model
zagadnień zaproponowany przez Kunza Rittela, obejmujący trzy typy węzłów, reprezentujących
zagadnienia, propozycje (zwane „pozycjami" — position) i argumenty.
JAD — Patrz połączony projekt aplikacji
Jeden do jednego (one-to-one association) — Skojarzenie o krotności 1 na obu końcach.
Jeden na wiele (one-to-many association) — Skojarzenie o krotności 1 na jednym końcu i 0. .n
lub l . . n n a drugim.
Jednolity proces wytwarzania oprogramowania (Unified Software Development Process)
— Iteratywny model cyklu życiowego oprogramowania, charakteryzujący się czterema
fazami zwanymi Inception („prapoczątek"), Elaboration („opracowywanie"), Construction
(„tworzenie") i Transition („przenoszenie").
Jednoznaczne wymaganie (unambiguous requirement) — Patrz wieloznaczność.
Język imperatywny (imperative language) — Patrz język proceduralny.
B.1. Terminologia 793

Język proceduralny (procedural language) — Język programowania, w którym określa się


explicite ciąg kroków niezbędnych do osiągnięcia pożądanego wyniku. Zwany także językiem
imperatywnym.
Język zapisu ograniczeń (Object Constraint Language — OCL) — Formalny język, definiowa-
ny jako część języka UML, służący do wyrażania ograniczeń w modelu obiektowym.
Joint Application Design — Patrz połączony projekt aplikacji.
Karta CRC (CRC card) — Karta indeksowa reprezentująca pojedynczą klasę. Akronim CRC
pochodzi od słów Class, Responsibilities i Collaborators („klasa, odpowiedzialność i [klasy]
współdziałające"). Nazwa klasy znajduje się w górnej części karty, zakres odpowiedzialności
klasy wyszczególniony jest w lewej kolumnie, w prawej natomiast widoczna jest lista klas,
za pomocą których dana klas wypełnia swe funkcje.
Katalog główny (master directory) — Biblioteka promocji.
Kierownik zespół (team leader) — Rola menedżerska odpowiedzialna za planowanie,
monitorowanie i kontrolowanie pracy pojedynczego zespołu.
Klarowność (clarity) — Patrz wieloznaczność.
Klasa (class) — Abstrakcja zbioru obiektów posiadających te same atrybuty, operacje, powią-
zania i semantykę. Klasa tym różni się od abstrakcyjnego typu danych, że może być wynikiem
specjalizacji innej klasy. Niektóre języki programowania, między innymi Ada i Modula,
udostępniają mechanizmy definiowania abstrakcyjnych typów danych; inne — Java, C++ czy
Smalltalk — umożliwiają definiowanie klas.
Klasa abstrakcyjna (abstract class) — Superklasa wykorzystywana wyłącznie na potrzeby
generalizacji, jej instancje nigdy nie są tworzone. Na diagramach nazwy klas abstrakcyjnych
pisane są kursywą.
Klasa bazowa (base class) — To samo, co superklasa.
Klasa implementacyjna (implementor class) — W kontekście wzorca projektowego —
klasa odpowiedzialna za niskopoziomowe zachowanie. W wielu wzorcach projektowych
zachowanie to realizowane jest w drodze współpracy wielu klas implementacyjnych.
Klasa kliencka (client class) — W kontekście wzorca projektowego — klasa odwołująca się
do jego interfejsu.
Klasa pochodna (derived class) — To samo, co subklasa.
Klasa rozszerzająca (extender class) — W kontekście wzorca projektowego — klasa rozsze-
rzająca klasę implementacyjną w celu dostarczenia odmiennej implementacji lub rozsze-
rzonego zachowania.
Klasa skojarzeniowa (association class) — Skojarzenie, któremu przypisano atrybuty
i operacje.
Klasa udoskonalona (refined class) — Patrz subklasa.
Klasa zdarzeniowa (event class) — Abstrakcja reprezentująca ogół zdarzeń wywołujących
taką samą reakcję systemu.
Klient (client) — Rola reprezentująca firmę lub osobę zamawiającą realizację systemu.
794 Dodatek B • Objaśnienia haseł

Klient lokalny, samodzielny (local king client) — Klient pozostający w ścisłym kontakcie
z programistami, mający samodzielność decyzyjną i posiadający głęboką wiedzę z zakresu
dziedziny aplikacyjnej.
Klient pełnomocnik (proxy client) —- Uczestnik działający w imieniu rzeczywistego klienta,
posiadający wiedzę wystarczającą do wyjaśniania im problemów z dziedziny aplikacyjnej,
lecz niemający pełnej swobody decyzyjnej.
Klient-serwer (client!server architectural style) — Styl architektoniczny, zgodnie z którym
użytkownicy komunikują się, za pomocą prostych aplikacji, z centralnym programem
serwera świadczącego usługi.
Klucz główny (primary key) — W systemie zarządzania bazą danych — minimalny zbiór
atrybutów, z natury jednoznacznie identyfikujący poszczególne rekordy tabeli.
Klucz kandydacki (candidate key) — W systemie zarządzania bazą danych zbiór atrybutów,
który może być wykorzystywany w charakterze klucza głównego.
Klucz obcy (foreign key) — W systemie zarządzania bazą danych atrybut lub zbiór atry-
butów tabeli odwołujący się do klucza głównego innej tabeli.
Kluczowy obszar procesu (Key Process Area — KPA) — Zbiór aktywności zmierzających
do osiągnięcia celu uważanego za istotny dla osiągnięcia danego stopnia dojrzałości procesu.
Przykładami takich obszarów są zarządzanie konfiguracją, zarządzanie zmianami w wymaga-
niach i zarządzanie ryzykiem.
Kod źródłowy (source code) — Reprezentacja systemu w języku programowania.
Kolekcja OCL (OCL collection) — Abstrakcyjny typ języka OCL, reprezentujący grupę
obiektów.
Komitet kontrolny (change control board) — Zespół kontrolujący żądania zmian w postaci
formalnego procesu.
Kompletność (completeness) — Właściwość modelu określająca, czy wszystkie istotne
zjawiska zostały w modelowaniu uwzględnione w postaci odpowiednich koncepcji.
Komponent (component) — Fizyczna i zastępowalna część systemu, zgodna z określonym
interfejsem. Przykładami komponentów są biblioteki klas, frameworki i moduły binarne.
Komunikacja (communication) — Aktywność, w ramach której następuje wymiana infor-
macji między uczestnikami, w sposób synchroniczny lub asynchroniczny, spontanicznie
lub zgodnie z uprzednim planem.
Komunikacja partnerska (peer-based communication structure) — Struktura komunikacyjna,
w ramach której programiści należący do różnych zespołów komunikują się bezpośrednio.
Komunikat (message) — Mechanizm, za pomocą którego jeden obiekt żąda od innego
obiektu wykonania określonej operacji. Struktura komunikatu obejmuje jego nazwę i pewną
liczbę argumentów. Obiekt odbierający komunikat identyfikuje go z odpowiadającą mu
operacją, którą następnie wywołuje z użyciem argumentów otrzymanych w komunikacie.
Koncepcja (concept) — Abstrakcja zbioru zjawisk posiadających wspólne cechy. Przykładowo
ta książka, Fryderyk Chopin czy walijski klub wędkarzy to przykłady zjawisk, natomiast
książki o obiektowo zorientowanej inżynierii oprogramowania, kompozytorzy i kluby wędka-
rzy są przykładami koncepcji. Nie należy więc mylić koncepcji ze zjawiskami.
B.1. Terminologia 795

Konfiguracja (configuration) — Określona wersja agregatu zarządzania konfiguracją.


Konsultant techniczny (technical consultant) — Rola konsultacyjna, której zadaniem jest
dostarczanie wiedzy eksperckiej z zakresu dziedziny realizacyjnej.
Kontrakt (contract) — Zbiór ograniczeń narzuconych na klasę lub komponent, umożli-
wiający współdzielenie przez komponent wywołujący i wywoływany tych samych założeń
dotyczących poszczególnych klas lub komponentów. Patrz także niezmiennik, warunek
wstępny, warunek końcowy.
Kontroler (controller) — Na gruncie architektury „model-widok-kontroler" podsystem
lub obiekt zarządzający sekwencjami interakcji z użytkownikiem.
Kontrolowanie zmian (change control) — Proces zatwierdzania albo odrzucania żądań
zmiany, mający na celu utrzymanie ewolucji systemu w zgodzie z jego celami projektowymi.
Konwersacje okazjonalne (hallway conversation) — Synchroniczny mechanizm komunikacji,
w ramach którego uczestnicy przypadkowo spotykają się twarzą w twarz.
Kończenie projektu (project termination) — Wieńczące projekt zaakceptowanie przez
klienta dostarczonego mu systemu.
KPA — Patrz kluczowy obszar procesu.
Krotka (tupie) — Uporządkowany zbiór wartości, wykorzystywany najczęściej do repre-
zentowania wartości atrybutów w relacyjnych bazach danych oraz uprawnień dostępu.
Krotność (multiplicity) — Zbiór liczb całkowitych przyporządkowany ustalonemu końcowi
skojarzenia, określający dopuszczalną liczbę łączy wychodzących z instancji klasy po tej stronie
skojarzenia. Przykładowo skojarzenie odzwierciedlające fakt, że samochód ma cztery koła,
posiada krotność 1 po stronie Samochód i krotność 4 po stronie Koło.
Kryterium (criterion.) —- Miara dobroci przy ocenie ewentualności jako potencjalnego
rozstrzygnięcia zagadnienia.
Kryterium kosztowe (cost criterion) — Cel projektowy związany z kosztami wytworzenia,
zainstalowania i eksploatacji systemu.
Kryterium pewności działania (dependability criterion) — Cel projektowy polegający na
minimalizowaniu częstotliwości awarii systemu i konsekwencji tych awarii. Kryterium
pewności działania obejmuje kryteria cząstkowe solidności, niezawodności, dostępności,
odporności na błędy, zabezpieczenia i bezpieczeństwo.
Kryterium pielęgnowalności (maintenance criterion) — Cel projektowy związany z łatwością
modyfikowania i aktualizowania systemu po jego dostarczeniu klientowi.
Kryterium użytkownika (end user criterion) — Cel projektowy związany z atrybutami
systemu widocznymi bezpośrednio dla użytkownika, niewymienionymi w kryteriach pewno-
ści działania i kryteriów wydajności.
Kryterium wydajnościowe (performance criterion) — Cel projektowy związany z szybkością
działania systemu lub jego zapotrzebowaniem na zasoby, czyli na przykład czasem reakcji,
przepustowością i wymaganiami pamięciowymi.
Kwalifikator (qualifier) — Atrybut używany do indeksowania skojarzenia kwalifikowanego.
Kwalifikowanie (qualification) — Technika redukowania krotności skojarzeń, polegająca na
zastępowaniu skojarzeń „wiele na wiele" i „jeden na wiele" przez skojarzenia kwalifikowane.
796 Dodatek B • Objaśnienia haseł

K w e s t i o n a r i u s z (questionnaire) — Synchroniczny m e c h a n i z m k o m u n i k a c y j n y , służący


p o z y s k i w a n i u o d u ż y t k o w n i k ó w w i e d z y z z a k r e s u d z i e d z i n y a p l i k a c y j n e j , za p o m o c ą p y t a ń
w strukturalnym układzie.

L i n i a b a z o w a (baseline) — U z g o d n i o n a i zweryfikowana wersja elementu konfiguracji.

L i s k o v z a s a d a — P a t r z z a s a d a z a s t ę p o w a n i a Liskov.

Lista k o n t r o l i d o s t ę p u (access control list — A C L ) — R e p r e z e n t a c j a m a c i e r z y k o n t r o l i


d o s t ę p u w f o r m i e listy p a r (aktor-operacja) stowarzyszonej z klasą objętą k o n t r o l o w a n i e m
d o s t ę p u . K a ż d a ze w s p o m n i a n y c h p a r o z n a c z a p r a w o k o n k r e t n e g o aktora do wykonywania
k o n k r e t n e j operacji n a p r z e d m i o t o w e j klasie.
Łącze (link) — Instancja skojarzenia, łącząca d w a obiekty.

Ł ą c z n i k (liaison) — R o l a k o m u n i k a c y j n a o d p o w i e d z i a l n a za p r z e p ł y w i n f o r m a c j i m i ę d z y
d w o m a zespołami. Przykładowo łącznik architektoniczny reprezentuje zespół f u n k c y j n y
przed zespołem architektonicznym.

Ł ą c z n i k a r c h i t e k t o n i c z n y (architecture liaison) — R o l a p r o g r a m i s t y c z n a p o l e g a j ą c a n a re-


p r e z e n t o w a n i u z e s p o ł u f u n k c y j n e g o p r z e d z e s p o ł e m architektonicznym. W i ą ż e się z transfe-
r o w a n i e m i n f o r m a c j i m i ę d z y zespołami, n e g o c j o w a n i e m z m i a n w interfejsach i z a p e w n i e n i e m
spójności A P I w obrębie całego systemu.

M a s z y n a s t a n u U M L (UML state machine) — Patrz diagram stanów.

M e c h a n i z m k o m u n i k a c y j n y (communication mechanism) — Narzędzie lub procedura wyko-


rzystywana d o wysyłania i odbierania informacji. M e c h a n i z m y k o m u n i k a c y j n e wspierają
j e d n o lub kilka zdarzeń k o m u n i k a c y j n y c h . Przykładami m e c h a n i z m ó w k o m u n i k a c y j n y c h
są telefonia, faks, p o c z t a e l e k t r o n i c z n a i groupware. M e c h a n i z m y komunikacyjne m o g ą być
s y n c h r o n i c z n e a l b o a s y n c h r o n i c z n e z a l e ż n i e o d t e g o , czy w y m a g a j ą k o i n c y d e n c j i c z a s o w e j
n a d a w c y i o d b i o r c y , czy też n i e .

M e n e d ż e r k o n f i g u r a c j i (configuration manager) — Rola p r o g r a m i s t y c z n a odpowiedzialna


za ś l e d z e n i e i a r c h i w i z o w a n i e p r o d u k t ó w o r a z o k r e ś l a n i e i c h w e r s j i („linii") b a z o w y c h .

M e t a m o d e l U M L (UML meta-model) — M o d e l reprezentujący konstrukcje języka UML.

M e t o d a (method) —- 1. W k o n t e k ś c i e t w o r z e n i a s y s t e m u — p o w t a r z a n a t e c h n i k a rozwiązy-
w a n i a specyficznego p r o b l e m u . P r z y k ł a d o w o p r z e p i s k u l i n a r n y s t a n o w i m e t o d ę s p o r z ą d z e n i a
o k r e ś l o n e j p o t r a w y 2. W k o n t e k ś c i e p r o g r a m o w a n i a o b i e k t o w e g o — i m p l e m e n t a c j a operacji.
P r z y k ł a d o w o S e t T i m e ( t ) jest m e t o d ą klasy Watch.

M e t o d a z a h a c z a n a (hook method) — M e t o d a d o s t a r c z a n a p r z e z klasę f r a m e w o r k u , p r z e z n a -


c z o n a d o p r z e d e f m i o w a n i a w s u b k l a s i e , w celu specjalizacji t e g o f r a m e w o r k u .

M e t o d o l o g i a (methodology) — K o l e k c j a m e t o d s ł u ż ą c y c h r o z w i ą z y w a n i u o k r e ś l o n e j klasy
p r o b l e m ó w . P r z y k ł a d o w o treść książki k u c h a r s k i e j dla c u k i e r n i k a jest m e t o d o l o g i ą w y p i e k u
ciast.

M e t o d o l o g i a R o y c e ' a (Royce's methodology) — A l t e r n a t y w n a m e t o d o l o g i a s t e r o w a n a ry-


zykiem, z a p r o p o n o w a n a przez Royce'a na bazie jednolitego procesu.

M i a r a d o b r o c i (goodness measure) — Jakościowe l u b ilościowe k r y t e r i u m o c e n y propozycji.


B.1. Terminologia 797

Model (model) — Abstrakcja systemu ułatwiająca jego zrozumienie poprzez pominięcie jego
szczegółów nieistotnych w danym kontekście. Przykładowo modelami systemu sprzedaży bi-
letów metra są: schemat okablowania dystrybutora biletów, podręcznik obsługi systemu
i model obiektowy jego oprogramowania.
Model akademicki (cathedral model) — Organizacja projektu opierająca się na planowaniu,
architekturze systemu i hierarchicznym przepływie informacji. Przykładem takiego modelu
jest organizacja głównego programisty. Przeciwieństwo modelu bazarowego.
Model analityczny (analysis model) — Model systemu, w założeniu poprawny, kompletny,
spójny i jednoznaczny. Model analityczny składa się z prostszych modeli — funkcjonalnego,
obiektowego i dynamicznego.
Model bazarowy (bazaar model) — Organizacja projektu oparta na dużej dynamice i rozpro-
szeniu współpracujących grup. Przeciwieństwo modelu akademickiego.
Model cyklu życiowego oprogramowania (software life cycle model) — Abstrakcja tworzenia
oprogramowania, której celem jest jego rozumienie, monitorowanie i kontrolowanie. Przy-
kładami modeli cyklu życiowego są model kaskadowy, V-model, model spiralny, jednolity
proces oraz model zagadnieniowy.
Model cyklu życiowego ukierunkowany na aktywności (activity-centered software life cycle
model) — Model cyklu życiowego oprogramowania reprezentujący przede wszystkim ak-
tywności związane z tworzeniem tego oprogramowania, w odróżnieniu od modelu cyklu
życiowego ukierunkowanego na encje.
Model cyklu życiowego ukierunkowany na encje (entity-centered software life cycle model)
— Model cyklu życiowego oprogramowania reprezentujący przede wszystkim produkty
wytwarzane w związku z realizacją projektu, w odróżnieniu od modelu cyklu życiowego
ukierunkowanego na aktywności.
Model dojrzałości organizacyjnej (Capability Maturity Model — CMM) — Framework
umożliwiający ocenę (pięciostopniową) stopnia dojrzałości organizacyjnej projektu.
Model dynamiczny (dynamie model) — Model opisujący te komponentu systemu, za pomocą
których manifestuje on swe zachowanie. W tej książce prezentujemy modele dynamiczne
w postaci diagramów stanów, diagramów sekwencji i diagramów aktywności.
Model funkcjonalny (functional model) — Model opisujący funkcjonalność systemu z per-
spektywy jego użytkownika. W tej książce reprezentujemy modele funkcjonalne w postaci
przypadków użycia.
Model katedralny — To samo, co model akademicki.
Model obiektowy (object model) — Model opisujący strukturę systemu w kategoriach
obiektów, atrybutów, skojarzeń i operacji. Na etapie zbierania i analizy wymagań model
obiektowy ma początkowo formę analitycznego modelu obiektowego, opisującego koncepcje
dziedziny aplikacyjnej odnoszące się do systemu. Na etapie projektowania systemu model
obiektowy rozszerzony jest do postaci modelu projektu systemu, obejmującej także opis
interfejsów poszczególnych podsystemów. Kolejnym etapem przeobrażenia modelu obiek-
towego jest model projektu obiektów, zawierający szczegółowy opis obiektów realizacyjnych.
798

Model projektu obiektów (object design model) — Szczegółowy model przedstawiający obiekty
aplikacyjne i obiekty realizacyjne będące częścią systemu. Zawiera szczegółowe definicje klas,
k o n t r a k t ó w , t y p ó w s y g n a t u r i z a k r e s ó w widzialności p o s z c z e g ó l n y c h a t r y b u t ó w i operacji.

M o d e l projektu systemu (system design model) — W y s o k o p o z i o m o w y opis systemu, obej-


m u j ą c y jego cele projektowe, dekompozycję na podsystemy, p l a t f o r m ę sprzętową i progra-
m o w ą , strategię zarządzania d a n y m i trwałymi, przepływ sterowania, politykę kontroli do-
stępu i t r a k t o w a n i e w a r u n k ó w granicznych. M o d e l p r o j e k t u s y s t e m u r e p r e z e n t u j e decyzje
strategiczne p o z w a l a j ą c e z e s p o ł o m n a e f e k t y w n ą w s p ó ł p r a c ę .
M o d e l racjonalizacji (rationale model) — T o s a m o , co m o d e l zagadnień.
M o d e l spiralny (spiral model) — I t e r a t y w n y i p r z y r o s t o w y m o d e l z a r z ą d z a n i a cyklem ży-
c i o w y m o p r o g r a m o w a n i a , k o n c e n t r u j ą c y się w o k ó ł z a r z ą d z a n i a ryzykiem.
M o d e l kaskadowy (waterfall model) - M o d e l cyklu życiowego oprogramowania, zgodnie
z k t ó r y m poszczególne p r o c e s y r o z w o j o w e realizowane są w s p o s ó b ściśle sekwencyjny.
M o d e l zadań (task model) — M o d e l p r a c y n a d p r o j e k t e m r e p r e z e n t o w a n y w postaci zadań
i zależności m i ę d z y n i m i .
M o d e l z a g a d n i e ń (issue model) — M o d e l r e p r e z e n t u j ą c y racjonalizację j e d n e j l u b kilku
decyzji w f o r m i e grafu. W ę z ł y tego grafu reprezentują zwykle zagadnienia, propozycje, argu-
menty, kryteria i rozstrzygnięcia.
M o d e l e s y s t e m u (system model) — Z b i ó r wszystkich m o d e l i ułatwiających r o z u m i e n i e
t w o r z o n e g o systemu. D o m o d e l i systemu należą m i ę d z y i n n y m i m o d e l analityczny, m o d e l
projektu systemu, m o d e l projektu o b i e k t ó w i k o d źródłowy.
M o d e l o w a n i e (modeling) — Aktywność polegająca n a tworzeniu abstrakcji systemu skupia-
jącej się wyłącznie n a aspektach systemu istotnych w d a n y m kontekście i ignorującej nieistotne
szczegóły. Ó w „kontekst" definiowany jest przez konkretne zadanie, n a potrzeby którego model
jest k o n s t r u o w a n y . P a t r z także abstrakcja.
M o d e l - w i d o k - k o n t r o l e r (Model/View/Controller - MVC) — T r ó j w a r s t w o w y styl archi-
t e k t o n i c z n y , w r a m a c h którego wiedza aplikacyjna u t r z y m y w a n a jest p r z e z o b i e k t y m o d e l u
i p r e z e n t o w a n a p r z e z obiekty w i d o k u , zaś z a r z ą d z a n i e n i ą o d b y w a się za p o ś r e d n i c t w e m
obiektów kontrolera. W tej książce obiekty m o d e l u nazywane są obiektami encji, a elementy
widoku — obiektami brzegowymi.
M o d y f i k o w a l n o ś ć (modifiability) — W ł a s n o ś ć systemu oznaczająca łatwość m o d y f i k o w a n i a
jego istniejących m o d e l i .
M T B F (Mean Time Between Failures) — 1. Przeciętny d y s t a n s czasu dzielący wystąpienie
d w ó c h kolejnych awarii systemu. 2. Przeciętny dystans czasu m i ę d z y w y k r y w a n i e m kolejnych
u s t e r e k w r a m a c h testowania.
M Y C — Patrz model-widok-kontroler.
N a d k l a s a — T o s a m o , co superklasa.
N a m i a s t k a t e s t o w a (test stub) — Częściowa i m p l e m e n t a c j a k o m p o n e n t u , o d k t ó r e g o za-
leżne są t e s t o w a n e k o m p o n e n t y . W y k o r z y s t y w a n a d o izolowania k o m p o n e n t ó w w trakcie
t e s t ó w j e d n o s t k o w y c h i t e s t ó w integracyjnych, dzięki c z e m u m o ż l i w e jest t e s t o w a n i e
k o m p o n e n t ó w zależnych o d i n n y c h k o m p o n e n t ó w , k t ó r e nie zostały jeszcze u k o ń c z o n e .
Nawigacja (navigation) — Kierunek trawersacji wynikającej ze skojarzeń między klasami
lub obiektami.
Nazwa (name) — 1. Obowiązkowy łańcuch znaków jednoznacznie identyfikujący klasę
w podsystemie. 2. Opcjonalny łańcuch znaków etykietujący skojarzenie między dwiema
klasami. 3. Obowiązkowy łańcuch znaków jednoznacznie identyfikujący atrybut lub me-
todę w ramach klasy.
NFR — (skrót od Non-Functional Requirement) — framework służący do śledzenia wy-
magań pozafunkcyjnych związanych z poszczególnymi decyzjami, ewentualnościami i relacji
między wymaganiami.
Niby-klient (pseudo client) — Członek organizacji działający w imieniu abstrakcyjnego
klienta, niebędacego organizacją ani osobą fizyczną. Brak konkretnego klienta charaktery-
styczny jest dla projektów, których rezultatami są produkty „na półkę". Niby-klient może
szybko podejmować niezbędne decyzje i z natury wpisuje się w ogólną strukturę projektu, ma
jednak z konieczności ograniczoną wiedzę w zakresie dziedziny aplikacyjnej.

Niezawodność (reliability) — Zdolność systemu lub komponentu do wypełniania żąda-


nych funkcji w określonym przedziale czasu, pod warunkiem spełnienia określonych wa-
runków. Wymagania określające niezawodność dotyczą między innymi średniego czasu
między awariami, wykrywalności specyficznych usterek lub zdolności powstrzymywania
ataków na zabezpieczenia.
Niezawodność oprogramowania (software reliability) — Własność systemu oznaczająca,
że jego oprogramowanie funkcjonować będzie bez awarii przez określony przedział czasu.
Niezmiennik (invariant) — Predykat zawsze prawdziwy dla wszystkich instancji klasy.
Notacja (notation) — Zbiór graficznych lub tekstowych zasad reprezentowania modelu.
Przykładem reprezentacji jest alfabet języka polskiego.
Notatka (note) — Komentarz towarzyszący diagramowi UML.
Obiekt (object) — Instancja klasy. Każdy obiekt jest jednoznacznie identyfikowany i prze-
chowuje własny zbiór wartości atrybutów.
Obiekt aplikacyjny (application object) — W modelu analitycznym obiekt reprezentujący
koncepcję z dziedziny aplikacyjnej. Nazywany często obiektem dziedziny aplikacyjnej.
Obiekt brzegowy (boundary object) — Obiekt reprezentujący interakcję między systemem
a użytkownikiem.
Obiekt dziedzinowy (domain object) — To samo, co obiekt aplikacyjny.
Obiekt encji (entity object) — Obiekt reprezentujący informację trwałą lub długoterminową

Obiekt modelu (model object) - Patrz model-widok-kontroler.


Obiekt projektowy (design object) — Patrz obiekt realizacyjny.
Obiekt realizacyjny (solution object) - Obiekt modelu projektu systemu lub modelu
projektu obiektów reprezentujący koncepcję z dziedziny realizacyjnej. Nazywany także
obiektem projektowym.
800 Dodatek B • Objaśnienia haseł

Obiekt sterujący (control object) — Obiekt reprezentujący zadanie wykonywane przez


użytkownika.
Obiekt uczestniczący (participating object) — Obiekt modelu analitycznego uwikłany w dany
przypadek użycia.
Obsługa wyjątków (exception handling) — Mechanizm tratowania wyjątków przez system.
OCL — Patrz język zapisu ograniczeń.
ODD — Patrz dokument projektu obiektów.
Odporność na usterki (fault tolerance) — 1. Metoda mająca na celu konstruowanie systemu
w taki sposób, by obecne w nim usterki nie powodowały jego załamania. 2. Zdolność systemu
do kontynuowania pracy mimo obecności usterek.
Odwołanie zwrotne (callback) — Wywołanie wskazanej operacji obiektu przez inny
obiekt, głównie w celu powiadomienia o zaistniałym zdarzeniu. Mechanizm ten jest między
innymi istotną częścią wzorca projektowego Obserwator, w ramach którego subskrybenci są
w taki właśnie sposób powiadamiani o zmianach stanu publikatora.
Odwzorowanie (mapping) — Matematyczne przyporządkowanie zbiorowi elementów
(lub pojedynczemu elementowi) jednego modelu elementu innego modelu.
Ograniczenie (constraint) — Reguła związana z elementem diagramu UML, precyzująca
szczegóły jego semantyki. Ograniczenia mogą być wyrażane bądź to w języku naturalnym,
bądź w postaci formalnej (na przykład w języku OCL).
Operacja (operation) — Niepodzielna jednostka zachowania prezentowanego przez klasę.
Obiekt zamierzający spowodować wykonanie operacji innego obiektu wysyła do tegoż
obiektu stosowny komunikat.
Organizacja (organization) — Zbiór zespołów, ról, ścieżek komunikacyjnych i struktury
raportowania dla konkretnego projektu (organizacja projektu) lub klasy projektów (orga-
nizacja wydziału lub korporacji).
Organizacja głównego programisty (chief programmer organization) — Hierarchiczna
organizacja projektu, w ramach której kierownik zespołu podejmuje wszystkie decyzje
techniczne o krytycznym znaczeniu.
Organizacja hierarchiczna (hierarchical organization) — Organizacja, w ramach której
struktury raportowania i podejmowania decyzji są hierarchiczne — decyzje podejmowane są
na najwyższym szczeblu organizacji (przez menedżera projektu) i komunikowane sukcesywnie
szczeblom niższym (kierownikom zespołów, którzy oznajmiają decyzje członkom zespołów).
Patrz także model akademicki.
Organizacja oparta na projektach (project-based organization) — Organizacja korporacyjna,
w której praca podzielona jest na poszczególne projekty: uczestnik zaangażowany jest w co
najwyżej jeden projekt.
Organizacja zespołowa (team-based organization) — Organizacja projektu, w ramach
której uczestnicy przypisani są do zespołów odpowiedzialnych za poszczególne podsystemy
lub funkcje projektowe.
Otwarte zagadnienie (open issue) — Zagadnienie, które nie zostało jeszcze rozstrzygnięte.
B.1. Terminologia 801

Pakiet (package) — W języku UML koncepcja grupowania wyrażająca fakt powiązania ze


sobą grupy klas lub obiektów. Pakiety wykorzystywane są na diagramach przypadków
użycia i diagramach klas w celu zmniejszenia stopnia komplikacji struktury, którą tworzą.
Pakiet pracy (work package) — Specyfikacja pracy, jaką należy wykonać w związku z realizacją
danego zadania lub aktywności.
Partycja (partition) — Podsystem w ramach architektury peer-to-peer.
Partycja aktywności (activity partition) — To samo, co tor pływacki.
Peer-to-peer (peer-to-peer architectural style) — Uogólnienie architektury klient-serwer,
zgodnie z którym każdy podsystem może pełnić rolę zarówno serwera, jak i klienta.
PERT (PERT chart) — Notacja przedstawiająca harmonogram w postaci acyklicznego grafu
zadań.
Pewność działania (dependability) — Własność systemu komputerowego określająca jego
niezawodność.
Pielęgnowalność (maintainability) — Łatwość przystosowywania systemu do nowych
technologii lub usuwania wykrytych w nim usterek.
Plan zarządzania konfiguracją oprogramowania (Software Configuration Management Plan
— SCMP) — Dokument definiujący procedury i konwencje zarządzania konfiguracją projektu:
identyfikowanie elementów konfiguracji, śledzenie ich statusu, aprobowanie żądań zmian
i audyt.
Plan zarządzania projektem programistycznym (Software Project Management Plan — SPMP)
— Dokument kontrolny projektu programistycznego definiujący aktywności, produkty,
„kamienie milowe" i zasoby przydzielone do projektu. SPMP często też definiuje procedury
menedżerskie i konwencje dotyczące projektu, takie jak raportowanie statusu, zarządzanie
ryzykiem czy plany awaryjne.
Planowanie testów (test planning) — Aktywność przydzielania zasobów na potrzeby te-
stowania i definiowania harmonogramu aktywności testowych.
Pluskwa (bug) — Patrz usterka.
Podklasa — To samo, co subklasa.
Podręcznik testera (Test Manual) — Dokument opisujący przypadki testowe dla systemu
wraz z rezultatami ich zastosowania.
Podręcznik użytkownika (User Manual) — Podręcznik opisujący interfejs użytkownika
systemu w taki sposób, by użytkownik nieznający systemu mógł łatwo nauczyć się jego obsługi.
Podsystem (subsystem) — Generalnie mniejsza, prostsza część dużego systemu. W kon-
tekście projektowania systemu — dobrze zdefiniowany komponent programowy, świadczący
usługi na rzecz innych podsystemów. Podsystemy mogą spełniać różnorodne zadania, między
innymi magazynowanie trwałych danych, realizację interfejsu użytkownika lub komunikację
sieciową między innymi podsystemami.
Połączony projekt aplikacji (Joint Application Design — JAD) — Technika zbierania wyma-
gań zakładająca współpracę klientów, użytkowników i programistów w konstruowaniu spe-
cyfikacji wymagań w trakcie trwającego przez tydzień zebrania roboczego.
802 Dodatek B • Objaśnienia haseł

Poprawka (correction) — Zmiana wprowadzona do komponentu w celu naprawienia tkwią-


cej w nim usterki.
Poprawność (correctness) — Właściwość modelu oznaczająca reprezentowanie przezeń
systemu dokładnie odzwierciedlającego potrzeby klienta i zamierzenia programistów.
Post mortem (postmortem review) — Planowe zdarzenie komunikacyjne mające na celu
kolekcjonowanie wiedzy zdobywanej w związku z realizacją zakończonego projektu.
Powiązania komunikacyjne (communication relationship) — Typ relacji na diagramach
przypadków użycia reprezentujących przepływ informacji między aktorem a przypadkiem
użycia.
Pozyskiwanie wiedzy (knowledge acquisition) — Aktywność polegająca na gromadzeniu
danych, organizowaniu ich do postaci czytelnej informacji i formalizowaniu tejże w formie
wiedzy.
Prezentacja problemu (problem presentation) — Planowe zdarzenie komunikacyjne, w ra-
mach którego klient i menedżer projektu przedstawiają uczestnikom wysokopoziomowe wy-
magania dotyczące systemu.
Proces (process) — Zbiór aktywności wykonywanych w dążeniu do osiągnięcia określonego
celu. Przykładami procesów są: zbieranie wymagań, analiza wymagań, zarządzanie projektem
i testowanie. Proces może być uważany za aktywność wysokopoziomową.
Procesy integralne (integral processes) — To samo, co procesy międzyrealizacyjne.
Procesy międzyrealizacyjne (cross-development processes) — Grupa procesów realizowanych
przez cały czas trwania projektu, zapewniających jakość i kompletność jego funkcji. Przy-
kładami takich procesów są walidacja, weryfikacja, zarządzanie konfiguracją, dokumentowanie
i szkolenie. Procesy międzyrealizacyj ne nazywane są także procesami integralnymi.
Produkt (work product) — Artefakt produkowany w procesie rozwojowym. Przykładami
produktów są: dokument analizy wymagań, dokument projektu systemu, prototypy inter-
fejsu użytkownika, dokumenty analizy rynku i — oczywiście — kompletny system.
Produkt finalny (deliverable) — Produkt przeznaczony dla klienta.
Produkt wewnętrzny (internal work product) — Produkt wytwarzany wyłącznie na we-
wnętrzne potrzeby projektu.
Programista (developer) — Rola, której zadaniem jest specyfikowanie, projektowanie i kon-
struowanie podsystemów. Przykładami programistów są: analityk, architekt systemu, pro-
jektant obiektów i implementator.
Programowanie bezosobowe (egoless programming) — Organizacja projektu zakładająca
równouprawnienie wszystkich członków projektu pod względem odpowiedzialności. W ramach
tej organizacji role programistów w zespole zazwyczaj zmieniają się stosownie do potrzeb.
Programowanie ekstremalne (Extreme Programming — XP) — Metodologia adresowana
do małych zespołów, zamierzających szybko tworzyć oprogramowanie w zmieniającym się
środowisku. Jej istotą jest jak największy stopień uwidocznienia projektu systemu w jego
kodzie źródłowym, co znacznie redukuje zarówno zapotrzebowanie na dokumentację, jak
i ryzyko powstania niespójności między poszczególnymi dokumentami.
B.1. Terminologia 803

Programowanie sterowane ryzykiem (risk-based development) — Cykl życiowy opro-


gramowania opierający się na wykrywaniu i minimalizowaniu czynników ryzyka. Patrz
także model spiralny.
Projektant (designer) — Patrz projektant obiektów.
Projektant obiektów (object designer) — Rola programistyczna odpowiedzialna za defi-
niowanie i doskonalenie interfejsów klas w ramach projektowania obiektów.
Projektowanie obiektów (object design) —- Aktywność, której celem jest wypełnienie luki
między modelem analitycznym a platformą sprzętową i programową. Programiści realizujący
tę aktywność definiują klasy, obiekty i interfejsy podsystemów, korzystają z gotowych kom-
ponentów, restrukturyzują model obiektowy w celu osiągnięcia określonych celów pro-
jektowych i optymalizują ów model pod względem wydajności. Produktem tej aktywności jest
model projektu obiektów.
Projektowanie systemu (system design) — Aktywność definiowania modelu projektu
systemu, z uwzględnieniem celów projektowych oraz dekompozycji systemu na mniejsze
podsystemy, z których każdy może być zrealizowany przez pojedynczy zespół. Rezultatem
projektowania systemu jest także wybór platformy sprzętowej i programowej, zdefiniowanie
strategii zarządzania danymi trwałymi, przepływu sterowania, polityki kontroli dostępu
i strategii traktowania warunków granicznych.
Projektowanie zorientowane obiektowo (object-oriented design) — Aktywność skoncen-
trowana na modelowaniu dziedziny realizacyjnej za pomocą obiektów.
Promocja (promotion) — Wersja przeznaczona na wewnętrzny użytek projektu, dostępna
tylko dla innych programistów. Patrz także emisja.
Propozycja (proposal) — Możliwe rozstrzygnięcie zagadnienia.
Propozycja zmiany — Inna nazwa żądania zmiany.
Protokolant (minute taker) — Rola, której zadaniem jest rejestrowanie przebiegu zebrania,
a szczególnie uzgodnionych rozstrzygnięć dyskutowanych zagadnień oraz ich implementacji
w postaci przydziału elementów działania poszczególnym uczestnikom.
Protokół zebrania (meeting minutes) — Dokument odzwierciedlający przebieg zebrania
i podejmowane na nim decyzje. Zawiera zwykle trzy części, poświęcone (kolejno) odnośni-
kom do poszczególnych punktów agendy, podjętym decyzjom i elementom działania przypi-
sanym poszczególnym uczestnikom.
Prototyp pionowy (vertical prototype) — Prototyp implementujący w sposób kompletny
(„głęboki") ograniczony zakres funkcjonalności, na przykład wszystkie obiekty interfejsu,
encji i sterowania dla jednego przypadku użycia.
Prototyp poziomy (horizontal prototype) — Prototyp implementujący w „płytki" sposób
szeroki zakres funkcjonalności, na przykład wyłącznie interfejsy użytkownika dla wielu
przypadków użycia.
Prototypowanie (prototyping) — Proces projektowania i realizowania uproszczonej wersji
systemu na potrzeby jej ewaluacji przez klienta lub menedżera projektu. Przykładowo prototyp
użyteczności ma na celu ocenę użyteczności różnych interfejsów systemu, a prototyp wy-
dajności służy do oceny wydajności różnych wariantów systemu.
804 Dodatek B • Objaśnienia haseł

Przegląd kliencki (client review) — Planowe zdarzenie komunikacyjne, w ramach którego


klient dokonuje monitorowania statusu projektu.
Przegląd partnerski (peer review) — Planowe zdarzenie komunikacyjne, w trakcie którego
programiści identyfikują i eliminują usterki we wstępnych wersjach produktów. Patrz także
inspekcja, wędrówka po kodzie.
Przegląd projektu (project review) — Planowe zdarzenie komunikacyjne, w ramach którego
menedżer monitoruje status projektu.
Przegląd statusu (status review) — Patrz zebranie statusowe.
Przejście (transition) — Możliwa zmiana stanu obiektu, wyzwalana przez zdarzenie, spełnienie
warunku lub upłynięcie zdefiniowanego odcinka czasu.
Przejście wewnętrzne (internal transition) — Na diagramie stanów UML — przejście nie-
prowadzące do zmiany stanu obiektu. Z przejściem wewnętrznym mogą być powiązane
akcje; wyzwolenie przejścia wewnętrznego nie powoduje jednak wykonania jakiejkolwiek
akcji początkowej lub akcji końcowej dla stanu, w którym obiekt wciąż pozostaje.
Przenośność (portability) — Łatwość transferowania systemu lub jego komponentu między
różnymi środowiskami sprzętowymi lub programowymi.
Przepływ danych (data flow) — Sekwencja, w jakiej dane są przesyłane, wykorzystywane
i przetwarzane. Patrz także przepływ sterowania.
Przepływ pracy (workflow) — W kontekście jednolitego procesu — wątek spójnych,
w większości sekwencyjnych aktywności prowadzących do wytworzenia artefaktu. Przepływy
pracy dzielą się na:
• inżynierskie — związane z przetwarzaniem wymagań, projektowaniem, imple-
mentacją, testowaniem i wdrażaniem;
• wspomagające — związane z zarządzaniem konfiguracją, zarządzaniem zmiana-
mi i środowiskiem.
Przepływ sterowania (control flow) — Sekwencja operacji wykonywanych przez system.
Patrz także przepływ danych.
Przepływ zdarzeń (flow of events) — Sekwencja zdarzeń w przypadku użycia opisująca
interakcję między aktorem a systemem.
Przepustowość (throughput) — Atrybut systemu wyrażający ilość pracy, jaką system ten
zdolny jest wykonać w jednostce czasu.
Przestrzeń robocza (workspace) — W kontekście zarządzania konfiguracją — biblioteka
promocji.
Przypadek testowy (test case) — Zbiór wartości wejściowych i spodziewanych wyników,
wykorzystywany w celu poszukiwania usterek tkwiących w testowanym komponencie.
Przypadek użycia (use case) — Uogólniona sekwencja interakcji między jednym lub kilkoma
aktorami a systemem. Patrz także scenariusz.
Pseudowymaganie (pseudo requirement) — Wymaganie pozafimkcyjne związane z implemen-
tacją, interfejsem, operacyjnością, pakietowaniem lub uregulowaniami prawnymi.
Punkt zaczepienia — To samo, co metoda zahaczana.
B.1. Terminologia 805

QOC — Questions, Options, and Criteria — model zagadnień zaproponowany przez McLeana,
rozszerzający model IBIS o reprezentowanie informacji związanej z kryteriami i oceną
zagadnień.
Questions, Options, and Criteria — Patrz QOC.
Racjonalizacja (rationale) — W kontekście modelowania — uzasadnienie podejmowa-
nych decyzji. Przykładowo wybór myDBMS jako systemu zarządzania bazą danych to decyzja
związana z projektem systemu; argumenty, iż myDBMS jest niezawodny i wystarczająco reak-
tywny dla osiągnięcia celów projektowych, stanowią część racjonalizacji tej decyzji; nazywana
także racjonalizacją projektu.
RAD — Patrz dokument analizy wymagań.
Raportowanie statusu (status accounting) — W kontekście zarządzania konfiguracją —
śledzenie żądań zmian, aprobowanie propozycji zmian i kolekcjonowanie racjonalizacji
dla każdej zmiany.
Reaktywność (responsivity) — Zwana także responsywnością — własność systemu, mie-
rzona odwrotnością czasu odpowiedzi na żądanie użytkownika — krótki czas odpowiedzi
to duża reaktywność.
Redaktor dokumentu (document editor) — Rola związana z integrowaniem dokumentacji.
Redaktor racjonalizacji (rationale editor) — Rola odpowiedzialna za kolekcjonowanie i or-
ganizowanie informacji składającej się na racjonalizację.
Refaktoring — To samo, co refaktoryzacja.
Refaktoryzacja (refactoring) — Transformacja kodu źródłowego, wykonywana w celu po-
prawy jego czytelności lub modyfikowalności, bez wpływu na zachowanie systemu. Re-
faktoryzacja zmierza do poprawienia projektu działającego systemu poprzez koncentrowanie
się na wybranych polach lub metodach poszczególnych klas.
Reinżynieria — To samo, co inżynieria wtórna
Relacja „ogół-szczegóły" (generalization-specialization relationship) — Patrz relacja
dziedziczenia.
Relacja dziedziczenia (inheritance relationship) — Relacja wiążąca klasę ogólną z klasą
bardziej wyspecjalizowaną. Specjalizacja klasy dokonuje się poprzez dodanie semantyki
i funkcjonalności do klasy ogólnej. Klasa ogólna nazywana jest w tym kontekście superklasą,
zaś klasa specjalizowana — subklasą.
Relacja rozszerzania (extend relationship) — Relacja między przypadkami użycia, zgodnie
z którą jeden przypadek użycia stanowi rozszerzenie przepływu zdarzeń w ramach drugiego.
Relacje rozszerzania wykorzystywane są zwykle do modelowania zachowań wyjątkowych,
takich jak pomocnicze elementy funkcjonalne czy obsługa wyjątków.
Relacja zawierania (include relationship) — Relacja między przypadkami użycia, zgodnie
z którą jeden przypadek użycia dokonuje wywoływania innego. Wywołanie to jest analogią
wywołania metody w języku programowania.
Repozytorium (repository) — 1. W kontekście architektury repozytoryjnej — centralny
podsystem odpowiedzialny za zarządzanie trwałymi danymi. 2. W kontekście zarządzania
konfiguracją — biblioteka emisji.
806 Dodatek B • Objaśnienia haseł

R e s p o n s y w n o ś ć — T o s a m o , co r e a k t y w n o ś ć R o l a (role) — 1. W k o n t e k ś c i e o r g a n i z a c j i
— z a k r e s o d p o w i e d z i a l n o ś c i w r a m a c h p r o j e k t u , p r z y p i s y w a n y o s o b i e l u b zespołowi. D a n a
o s o b a m o ż e w y p e ł n i a ć kilka ról. P r z y k ł a d a m i ról są: analityk, a r c h i t e k t s y s t e m u , tester, p r o -
g r a m i s t a , m e n e d ż e r i w e r y f i k a t o r . 2. W k o n t e k ś c i e s k o j a r z e ń — ł a ń c u c h z n a k ó w o k r e ś l a j ą c y
z n a c z e n i e k l a s y dla s k o j a r z e n i a , w k t ó r e jest u w i k ł a n a .

R o l a k o n s u l t a c y j n a (consultant role) — K a ż d a z ról, k t ó r e j z a d a n i e m jest w s p i e r a n i e


u c z e s t n i k ó w p r o j e k t u , w p o s t a c i d o r a ź n e g o u z u p e ł n i a n i a b r a k u j ą c e j w i e d z y specjalistycznej.
Role k o n s u l t a c y j n e t o p r z e d e w s z y s t k i m r o l e klienta, u ż y t k o w n i k a , k o n s u l t a n t a t e c h n i c z n e g o
i s p e c j a l i s t y z d z i e d z i n y aplikacyjnej.

R o l a m e n e d ż e r s k a (management role) — K a ż d a r o l a z w i ą z a n a z p l a n o w a n i e m , m o n i t o r o -
w a n i e m i k o n t r o l o w a n i e m p r o j e k t u . P r z y k ł a d a m i r ó l m e n e d ż e r s k i c h są m e n e d ż e r p r o j e k t u
i kierownik zespołu. Patrz także zarządzanie projektem.

R o l a m i ę d z y f u n k c y j n a (cross-functional role) — K a ż d a r o l a z w i ą z a n a z k o o r d y n o w a n i e
pracy kilku zespołów. Przykłady takich ról to m e n e d ż e r konfiguracji, łącznik architekto-
niczny, tester i redaktor dokumentacji.

R o z p r o s z o n e , j e d n o c z e s n e groupware (same time, different place groupware) — Patrz


groupware.

R o z p r o s z o n e , n i e j e d n o c z e s n e groupware (different time, different place groupware) — Patrz


groupware.

R o z s t r z y g a n i e z a g a d n i e n i a (issue resolution) — Nieplanowane zdarzenie komunikacyjne,


w r a m a c h którego uczestnicy dążą do osiągnięcia zgody w sprawie zagadnienia lub znale-
zienia dla niego rozstrzygnięcia.

R o z s t r z y g n i ę c i e (resolution) — P r o p o z y c j a w y b r a n a p r z e z u c z e s t n i k ó w w celu z a m k n i ę c i a
zagadnienia.

R o z s z e r z a l n o ś ć (extensibility) — W ł a s n o ś ć s y s t e m u określająca, j a k łatwe jest jego p r z y s t o s o -


wywanie do nowej funkcjonalności.

R o z w i ą z y w a n i e p r o b l e m u (problem solving) — Poszukiwanie i ocenianie ewentualności


rozwiązania p r o b l e m u , często m e t o d ą p r ó b i błędów.

R o z w i d l e n i e (fork node) — N a d i a g r a m i e a k t y w n o ś c i U M L w ę z e ł k o n t r o l n y r e p r e z e n t u j ą c y
rozszczepienie sterowania n a kilka wątków.

R o z w ó j o p a r t y n a z a g a d n i e n i a c h (issue-based development) — Patrz zagadnieniowy model


cyklu życiowego.

R y z y k o (risk) — O b s z a r n i e p e w n o ś c i m o g ą c e j p r o w a d z i ć d o r o z b i e ż n o ś c i p r o j e k t u z p l a n e m
— opóźnień w harmonogramie, niespełnienia wymagań, przekroczenia budżetu i tym po-
d o b n y c h , a w k o n s e k w e n c j i załamania całego projektu.

S c e n a r i u s z (scenario) — I n s t a n c j a p r z y p a d k u użycia. S c e n a r i u s z r e p r e z e n t u j e k o n k r e t n ą
sekwencję interakcji między systemem a j e d n y m lub kilkoma aktorami.

S c e n a r i u s z b i e ż ą c y (as-is scenario) — S c e n a r i u s z o p i s u j ą c y z d a r z e n i e d o t y c z ą c e istniejącego


s y s t e m u . S c e n a r i u s z e t a k i e m a j ą n a celu w e r y f i k o w a n i e p r a w i d ł o w e g o r o z u m i e n i a s y s t e m u
przez użytkowników.
B.1. Terminologia 807

Scenariusz ewaluacyjny (evaluation scenario) — Scenariusz opisujący instancję zadania


użytkownika w kontekście systemu podlegającego ocenie.
Scenariusz treningowy (training scenario) — Przewodnik służący przystosowaniu nowych
użytkowników do pracy z systemem, krok po kroku instruujący użytkownika w kwestii
wykonywania poszczególnych zadań.
Scenariusz wizjonerski (visionary scenario) — Scenariusz opisujący fragment systemu
istniejącego dopiero w zamyśle projektantów. Taki scenariusz może być zarówno punktem
w przestrzeni modelowania programistów, którzy w ten sposób doskonalą swe wyobraże-
nie na temat przyszłego systemu, jak i medium komunikacji z użytkownikami na etapie
zbierania wymagań.
Scentralizowane sterowanie ruchem (Centralized Traffic Control — CTC) — Zestaw procedur
i systemów umożliwiający dyspozytorom zdalne monitorowanie ruchu pociągów i sterowanie
nim z centrum dyspozycyjnego (nastawni).
Schemat (schema) — W kontekście systemu zarządzania bazą danych — metamodel danych.
SCMP — Patrz plan zarządzania konfiguracją oprogramowania.
SDD — Patrz dokument projektu systemu.
Sekwencja OCL — To samo, co ciąg OCL.
Skojarzenie (association) — Relacja między dwiema lub kilkoma klasami określająca możliwe
łącza między instancjami tych klas. Skojarzenia opatrywane są nazwami, można im także
przyporządkowywać krotności i role na każdym z końców.
Skojarzenie kwalifikowane (qualified association) — Skojarzenie, którego jeden koniec
indeksowany jest przez atrybut. Przykładowo skojarzenie między katalogiem a zawartymi
w nim plikami indeksowane jest przez nazwę pliku po stronie Katal og.
Solidność (robustness) — Stopień zdolności systemu lub komponentu do poprawnego funk-
cjonowania w obliczu niepoprawnych danych lub niesprzyjających warunków środowiska.
Specjalista z dziedziny aplikacyjnej (application domain specialist) — Rola konsultacyjna
odpowiedzialna za dostarczenie na użytek projektu wiedzy eksperckiej z zakresu dziedziny
aplikacyjnej.
Specjalista z dziedziny realizacyjnej (solution domain specialist) — Rola konsultacyjna
odpowiedzialna za dostarczenie na użytek projektu wiedzy eksperckiej z zakresu rozwiązań
implementacyjnych. Wiedza ta obejmuje między innymi algorytmy, struktury danych, procesy,
technologie implementacyjne i środowiska programistyczne.
Specjalizowanie (specialization) — Aktywność modelowania polegająca na kreowaniu
szczegółowej koncepcji na podstawie koncepcji bardziej ogólnej.
Specyfikacja wymagań (requirements specification) — Kompletny i precyzyjny opis systemu
z perspektywy jego użytkownika. Specyfikacja wymagań obejmuje przypadki użycia i scena-
riusze, i w przeciwieństwie do modelu analitycznego jest zrozumiała dla użytkownika.
SPMP — Patrz plan zarządzania projektem programistycznym.
Spoistość (cohesion) — Stopień powiązania elementów składowych klasy lub podsystemu.
Duża spoistość jest wysoce pożądana jako gwarantująca spójną modyfikację powiązanych klas.
808 Dodatek B • Objaśnienia haseł

Spójność (consistency) — Właściwość modelu oznaczająca brak jego wewnętrznej sprzeczności


— model jest niespójny, jeżeli prezentuje kilka niezgodnych ze sobą widoków systemu.
Sprzężenie (coupling) — Stopień powiązania elementów należących do różnych klas lub
podsystemów. Małe sprzężenie jest wysoce pożądane, jako minimalizujące wpływ zmian
wprowadzanych do jednego podsystemu na inne podsystemy.
Stan (state) — Warunek oznaczający konkretną wartość atrybutu obiektu lub podsystemu.
Statyczna kontrola dostępu (static access control) — Polityka kontroli dostępu determi-
nowana bezpośrednio w kodzie źródłowym i nieulegająca zmianie po jego skompilowaniu.
Przeciwieństwo dynamicznej kontroli dostępu.
Stereotyp (stereotype) — Mechanizm rozszerzający język UML, wykorzystywany do klasyfi-
kowania elementów modeli. Stereotyp ma formę napisu (łańcucha znaków) ujętego w ogra-
niczniki « » (na przykład «control») zlokalizowanego w pobliżu elementu, do którego się
odnosi (zwykle klasy lub skojarzenia). Formalnie przyporządkowanie stereotypu elementowi
modelu jest równoważne stworzeniu nowej klasy w metamodelu języka UML; przykłado-
wo towarzyszący obiektowi stereotyp «control» oznacza, że obiekt ten jest obiektem steru-
jącym przypadku użycia.
Sterowanie oparte na zdarzeniach (event-driven control) — Paradygmat przepływu ste-
rowania, zgodnie z którym zdarzenia wykrywane przez główną pętle programu delegowane
są w celu ich obsłużenia do odpowiednich obiektów.
Sterowanie proceduralne (procedure-driven control) — Paradygmat przepływu sterowania
organizowanego przez procedury oczekujące na wprowadzenie danych. Kolejność wyko-
nywania operacji jest zdeterminowana kolejnością wywoływania procedur.
Sterownik testowy (test driver) — Częściowa implementacja komponentu, którego działanie
uzależnione jest od testowanego komponentu; wykorzystywana na potrzeby testów jednost-
kowych i testów integracyjnych.
Struktura podziału pracy (Work Breakdown Structure — WBS) — Hierarchiczna dekompo-
zycja pracy na zadania. Liście tej hierarchii reprezentują konkretne zadania przydzielane
uczestnikom, zaś węzły pośrednie reprezentują pracę związaną z poszczególnymi produktami.
Struktura raportowania (reporting structure) — Struktura reprezentująca łańcuch przepływu
informacji i raportowania statusu.
Styl architektoniczny (architectural style) — Ogólny model projektowy, stanowiący punkt
wyjścia do tworzenia modelu projektu systemu. Najbardziej znanymi stylami architekto-
nicznymi są klient-serwer, peer-to-peer, potoki i filtry oraz „model-widok-kontroler".
Subklasa (subclass) — W relacji generalizowania specjalizowana klasa pochodna superklasy.
Superklasa (superclass) — W relacji generalizowania uogólniona klasa nadrzędna subklasy.
Sygnatura (signature) — W kontekście operacji klasy — krotka złożona z typów kolejnych
parametrów tej operacji i typu zwracanego przez nią wyniku. Sygnatury operacji definiowane
są na etapie projektowania obiektów.
Synchroniczność (synchronicity) — Patrz mechanizm komunikacyjny, groupware.
B.1. Terminologia 809

System (system) — Zorganizowany zbiór komunikujących się części, zaprojektowany z intencją


osiągnięcia określonych celów. Przykładowo samochód, składający się z czterech kół, pod-
wozia, silnika i karoserii, zaprojektowany został z myślą o przewożeniu osób, natomiast
zegarek, złożony z baterii, obwodu scalonego, przycisków i wyświetlacza, służy do pomiaru
upływającego czasu.
Szybkość projektu (project velocity) — W rozumieniu programowania ekstremalnego —
miara szybkości postępowania prac w projekcie, wyrażająca się ilością tygodni idealnych
zrealizowanych w tygodniu roboczym.
Szyfrowanie (encryption) — Przekształcanie komunikatu z postaci jawnej (zwanej tekstem
otwartym) na postać zakodowaną, zwaną szyfrogramem, dzięki czemu treść tego komuni-
katu staje się bezużyteczna dla osób nieuprawnionych do jego odczytywania, nieznających
klucza wspomnianego kodowania.
Ścieżka krytyczna (critical path) — 1. W modelu zadań — najdłuższa ścieżka, mierzona
sumarycznym czasem realizacji zadań. Jakiekolwiek opóźnienie w realizacji zadania należącego
do tej ścieżki przekłada się na opóźnienie całego harmonogramu. 2. W grafie PERT — naj-
krótsza ścieżka między węzłem początkowym a końcowym, mierzona liczbą węzłów.
Ścisłe dziedziczenie (strict inheritance) — Relacja dziedziczenia zgodna z zasadą zastę-
powania Liskov.
Środowisko (environment) — Zbiór elementów istniejących w momencie rozpoczynania
projektu.
Test białoskrzynkowy (whitebox test) — Test koncentrujący się na wewnętrznej strukturze
komponentu. Przeciwieństwo testu czarnoskrzynkowego.
Test czarnoskrzynkowy (blackbox test) — Test abstrahujący od implementacji komponentu,
skupiający się najego zachowaniu oraz produkowanych przez niego wynikach na podstawie
określonych danych wejściowych. Przeciwieństwo testu białoskrzynkowego.
Tester (tester) — Rola koncentrująca się na planowaniu, projektowaniu, wykonywaniu i ana-
lizowaniu testów.
Testowanie (testing) — Aktywność, w ramach której programiści znajdują różnice między
systemem a jego modelami, za pomocą jego uruchamiania (być może częściowego) z przykła-
dowymi zestawami danych. Na testowanie składają się testy jednostkowe, testy integracyjne,
testy systemowe i testy użyteczności.
Testowanie akceptacyjne (acceptance testing) — Aktywność, za pomocą której klient decyduje,
czy system spełnia kryteria akceptacyjne.
Testowanie funkcjonalności (functional testing) — Aktywność testowania systemu, w ra-
mach której programiści wykrywają rozbieżności między funkcjonalnością systemu a mo-
delem przypadków użycia.
Testowanie hurtowe (big bang testing) — Strategia testowania integracyjnego zakładająca
jednoetapowe zintegrowanie i testowanie komponentów, z których każdy z osobna przeszedł
testowanie jednostkowe.
Testowanie instalacyjne (installation testing) — Aktywność wspólnego testowania systemu
przez klienta i programistów w jego docelowym środowisku operacyjnym.
810 Dodatek B • Objaśnienia haseł

Testowanie integracyjne (integration testing) — Testowanie sukcesywnie powiększanych


grup podsystemów w celu wykrycia usterek w samych podsystemach oraz w ich współpracy.
Testowanie jednostkowe (unit testing) — Testowanie osobno poszczególnych komponentów.
Testowanie kanapkowe (sandwich testing) — Strategia testowania integracyjnego, stanowiąca
kombinację testowaniawstępującego i zstępującego.
Testowanie pilotażowe (pilot testing) — Aktywność testowania systemu przez grupę wybra-
nych użytkowników w środowisku wdrażania systemu.
Testowanie polowe (field testing) — To samo, co testowanie pilotażowe.
Testowanie regresyjne (regression testing) — Powtórne uruchomienie testów integracyjnych
w celu upewnienia się, że wniesiona do systemu poprawka nie spowodowała reaktywacji
usuniętych wcześniej usterek, czyli sprawdzenie, czy po wniesieniu poprawki system nadal
zalicza testy integracyjne, które zaliczał przed jej wniesieniem.
Testowanie strukturalne (structural testing) — Aktywność testowania polegająca na wy-
szukiwaniu różnic między modelem projektu systemu a samym systemem.
Testowanie systemu (system testing) — Testowanie wszystkich podsystemów jako całości
tworzącej kompletny system. Testowanie systemu obejmuje testy funkcjonalności, testy
wydajnościowe, testy akceptacyjne i testy instalacyjne.
Testowanie użyteczności (usability testing) — Walidacja systemu lub modelu, wykonywana
przez użytkowników na podstawie prototypów i symulacji systemu.
Testowanie wstępujące (bottom-up testing) — Strategia testowania integracyjnego, zgodnie
z którą komponenty integrowane są przyrostowo, począwszy od najniższego poziomu. Te-
stowanie takie nie wymaga namiastek testowych. Przeciwieństwo testowania zstępującego.
Patrz także testowanie kanapkowe.
Testowanie wydajnościowe (performance testing) — Aktywność testowania zmierzająca
do odnajdywania różnic między odmiennymi aspektami rzeczywistej wydajności systemu
a wydajności określonej przez wymagania pozafunkcyjne.
Testowanie wymagań (requirements testing) — To samo, co testowanie funkcjonalności.
Testowanie zstępujące (top-down testing) — Strategia testowania integracyjnego, zgodnie
z którą komponenty integrowane są przyrostowo, począwszy od najwyższego poziomu.
Testowanie takie nie wymaga sterowników testowych. Przeciwieństwo testowania wstępują-
cego. Patrz także testowanie kanapkowe.
Tor pływacki (swimlane) — W języku UML koncepcja grupowania wyróżniająca aktyw-
ności wypełniane przez ten sam obiekt lub tę samą grupę obiektów.
Transformacja (transformation) — Odwzorowywanie jednego modelu na inny, w celu
poprawienia konkretnej właściwości oryginalnego modelu (na przykład modularności)
przy zachowaniu innych własności (na przykład funkcjonalności). Transformacja jest
zwykle zlokalizowana, dotyczy niewielkiej liczby klas, atrybutów lub operacji i wykonywana
jest jako sekwencja małych kroków.
Transformacja modelu (model transformation) — Transformacja modelu obiektowego,
której rezultatem jest inny model obiektowy. Celem takiej transformacji jest uproszczenie
lub zoptymalizowanie modelu i uczynienie go w ten sposób bliższym spełnienia wymagań
zawartych w specyfikacji.
B.1. Terminologia 811

Transition — Patrz jednolity proces wytwarzania oprogramowania.


Trwałe dane (persistent data) — Dane istniejące między kolejnymi uruchomieniami systemu.
Typ (type) — Zbiór wartości dopuszczalnych dla atrybutu lub zmiennej.
Typ atrybutu (attribute type) — Patrz typ.
Typ danych (data type) — Abstrakcja zbioru wartości w kontekście języka programowania.
Przykładowo i nt jest typem danych w języku Java, reprezentującym liczby całkowite.
Uczestnik (participant) — Każda osoba zaangażowana w realizację projektu programi-
stycznego.
Ukryte skojarzenia (buried association) — W kontekście odwzorowywania modelu
obiektowego na schemat relacyjnej bazy danych — klucz obcy reprezentujący skojarzenia
„jeden na wiele" i „wiele na wiele".
UML — Patrz Unified Modeling Language.
Umowa projektu (project agreement) — Dokument definiujący w sposób formalny zakres,
czas trwania, koszt i produkty finalne projektu. Wymagania zawarte w umowie projektu
podzielone zostają zwykle na trzy kategorie:
• wymagania podstawowe — te, które system absolutnie musi spełniać,
• wymagania dodatkowe — dotyczące cech pożądanych w systemie, aczkolwiek
niekoniecznych,
• wymagania opcjonalne — te, których spełnienie jest możliwe, lecz niekonieczne
w przyszłości.
Umowa projektu jest rozwinięciem deklaracji problemu.
Unified Modeling Language — Standardowa notacja do reprezentowania diagramów.
Unikanie usterek (fault avoidance) — Metoda mająca na celu zapobieganie wprowadzaniu
usterek do tworzonego systemu. Przykładami takich metod są: zarządzanie konfiguracją,
weryfikacje i przeglądy produktów oraz stosowanie odpowiednich metodologii programi-
stycznych.
Uprawnienie dostępu (access right) — Zawartość komórki macierzy kontroli dostępu,
określająca uprawnienia konkretnego aktora w stosunku do konkretnej klasy.
Usługa (service) — Zbiór powiązanych operacji oferowanych przez podsystem.
Usterka (fault) — Mechaniczna lub algorytmiczna przyczyna błędu, zwana także pluskwą
lub defektem.
Uwierzytelnianie (authentication) — Proces kojarzenia osoby z uprawnieniami dostępu.
Użyteczność (usability) — Łatwość, z jaką użytkownik opanowuje operowanie systemem,
przygotowywanie dla niego danych wejściowych i interpretowanie wyników.
Użytkownik (end user) — Rola, którą pełni osoba używająca dostarczonego systemu.
Użytkownik klasy (class user) — Programista wykorzystujący operacje realizowane przez
daną klasę na potrzeby realizacji innej klasy, zwanej klasą kliencką.
Y-Model — Odmiana modelu kaskadowego, uwidaczniająca zależności między procesami
rozwojowymi a procesami weryfikacyjnymi.
812 Dodatek B • Objaśnienia haseł

Walidacja (validation) — Aktywność mająca na celu zapewnienie, że system spełnia po-


trzeby użytkowników.
Wariant (variant) — Wersja przeznaczona do współistnienia z innymi wersjami. Współist-
nienie różnych wersji systemu może wynikać z eksploatowania systemu na różnych patfor-
mach sprzętowych lub systemowych (Linuksie, Windows, Solarisie) lub ze zróżnicowanego
zakresu funkcjonalności (przykładowo — standardowej, profesjonalnej i korporacyjnej).
Warstwa (layer) — Jeden z podsystemów powstających w wyniku hierarchicznej dekom-
pozycji systemu. Każda warstwa zależna jest wyłącznie od warstw niższych i nie posiada
żadnej wiedzy na temat warstw wyższych.
Warunek brzegowy — To samo, co warunek graniczny.
Warunek graniczny (boundary condition) — Specjalny warunek, zwany także warunkiem
brzegowym, który musi zostać poprawnie zinterpretowany przez system. Do warunków
granicznych zaliczamy między innymi uruchamianie i zamykanie systemu oraz występujące
w czasie jego pracy sytuacje wyjątkowe.
Warunek końcowy (postcondition) — Predykat, którego prawdziwość jest gwarantowana
po zakończeniu wywołanej operacji.
Warunek wejściowy (entry condition) — Warunek, który musi zostać spełniony przed zaini-
cjowaniem przypadku użycia.
Warunek wstępny (precondition) — Predykat, którego prawdziwość jest wymagana przed
wywołaniem operacji.
Warunek wyjściowy (exit condition) — Warunek, którego prawdziwość gwarantowana
jest po zakończeniu wykonywania przypadku użycia.
Wątek (thread) — Paradygmat przepływu sterowania, zgodnie z którym w systemie uru-
chamiana jest dowolna liczba wątków w celu równoległego obsługiwania wielu kanałów
danych wejściowych.
WBS — Patrz struktura podziału pracy.
Wersja (version) — Stan elementu konfiguracji lub agregatu CM w ściśle określonym
punkcie czasowym. Wersja agregatu CM nazywana jest konfiguracją.
Weryfikacja (verification) — Zbiór formalnych metod zmierzających do wykrywania usterek
systemu bez jego uruchamiania.
Weryfikator (reviewer) — Rola uczestnika weryfikującego produkty pod kątem kryteriów
jakościowych — kompletności, poprawności, spójności i klarowności.
Weryfikowalność (verifiability) — Właściwość modelu oznaczająca możliwość uznania go
za prawdziwy albo fałszywy.
Wędrówka po kodzie (walkthrough) — Planowe zdarzenie komunikacyjne, w ramach
którego programista prezentuje kod źródłowy instrukcja po instrukcji, a inni programiści
wyrażają swe opinie i wątpliwości związane z tą prezentacją. Patrz także inspekcja.
Węzeł kontrolny (control node) — Na diagramie aktywności UML węzeł koordynujący
przepływ sterowania. Patrz węzeł decyzyjny, rozwidlenie, złączenie.
B.1. Terminologia 813

Widok (view) — 1. W kontekście modelowania — podzbiór elementów modelu, łatwiej-


szy w zrozumieniu niż oryginalny model. 2. W kontekście stylu architektonicznego
model-widok-kontroler — zbiór obiektów odpowiedzialnych za wyświetlanie informacji
odbieranych z obiektów modelu.
Widzialność atrybutu (attribute visibility) — Stopień dostępności atrybutu z poziomu in-
nych klas.
Wiele na wiele (many-to-many association) — Skojarzenie o krotnościach 0. .n lub 1. „n
na obu końcach.
Wielozbiór OCL (OCL bag) — Kolekcja w języku OCL reprezentująca nieuporządkowany
wielozbiór, czyli zbiór, którego elementom przypisane są krotności występowania. Wielo-
zbiory wykorzystywane są w języku OCL do reprezentowania wyników nawigacji po ciągach
skojarzeń.
Wieloznaczność (ambiguity) — Własność modelu polegająca na tym, że jedna koncepcja
odnosi się do dwóch lub więcej niepowiązanych ze sobą zjawisk.
Wspieralność (supportability) — Łatwość modyfikowania systemu po jego wdrożeniu.
Wydajność (performance) — Wartość ilościowego atrybutu systemu — przepustowości,
czasu reakcji, dostępności, dokładności i tym podobnych.
Wyjątek (exception) — Niespodziewane zdarzenie, zaistniałe w trakcie wykonywania pro-
gramu.
Wykonalność (realizability) — Własność modelu oznaczająca, że reprezentowany przez
niego system da się zrealizować.
Wykres Gantta (Gantt chart) — Notacja odzwierciedlająca harmonogram projektu progra-
mistycznego na osi czasowej. Równolegle do tej osi, zorientowanej poziomo, rysowane są
słupki oznaczające poszczególne zadania: początek i koniec słupka oznaczają rozpoczęcie
i zakończenie odnośnego zadania.
Wykrywanie usterek (fault detection) — Metoda mająca na celu wykrywanie usterek
tkwiących w działającym systemie. Przykładami takich metod są debugowanie i testowanie.
Wymagania interfejsu (interface requirement) — Ograniczenia narzucane przez zewnętrzne
systemy i formaty wymiany danych.
Wymagania prawne (legal requirement) — Ograniczenia dotyczące licencjonowania, certyfi-
katów i generalnie zgodności z obowiązującymi przepisami. Przykładem wymagań tej ka-
tegorii może być wymóg zgodności oprogramowania opracowywanego na potrzeby rządu
USA z sekcją 508 ustawy Rehabilitation Act z 1973 roku, zobowiązującej instytucje publiczne
do zapewnienia dostępności ich serwisów informacyjnych i usług elektronicznych, ze szczegól-
nym uwzględnieniem potrzeb osób niepełnosprawnych.
Wymaganie (requirement) — Funkcja, której wypełniania żąda się od systemu (wymaganie
funkcyjne) albo widoczne dla użytkownika ograniczenie narzucone na system (wymaganie
pozafunkcyjne).
Wymaganie funkcyjne (functional requirement) — Obszar funkcjonalności, którego reali-
zacji wymaga się od systemu. Wymagania funkcyjne opisują interakcje między systemem
a aktorami w sposób niezależny od realizacji tego systemu.
814 Dodatek B • Objaśnienia haseł

Wymaganie implementacyjne (implementation requirement) — Ograniczenie narzucone


na implementację systemu, dotyczące użycia specyficznych narzędzi, konkretnego języka
programowania, platformy sprzętowej i tym podobnych.
Wymaganie operacyjne (operations requirement) — Ograniczenie związane z administrowa-
niem i zarządzaniem systemem.
Wymaganie pakietowe (packaging requirement) — Ograniczenie związane z formą do-
starczenia produktu, na przykład z nośnikami instalacyjnymi systemu.
Wymaganie pozafunkcyjne (nonfunctional requirement) — Wymaganie związane z wi-
docznymi dla użytkownika aspektami systemu, abstrahujące jednak od jego elementów
funkcjonalnych. Patrz także cel projektowy.
Wysokopoziomowy projekt systemu (top-level design) — Wstępna dekompozycja systemu
na podsystemy, wykorzystywana na potrzeby formowania zespołów i planowania projektu.
Wywiad strukturalny (structured interview) — Wywiad oparty na kwestionariuszu, mający
na celu wyjaśnienie niekompletnych lub niejednoznacznych odpowiedzi.
Wzorzec architektoniczny (architectural pattern) — Patrz styl architektoniczny.
Wzorzec projektowy (design pattern) — Szablonowe rozwiązanie, umożliwiające (po odpo-
wiednim przystosowaniu) rozpracowywanie częstych problemów projektowych. Elementami
każdego wzorca projektowego są: nazwa, opis problemu, dostarczane rozwiązanie i opis jego
konsekwencji. Wykorzystywane w tej książce wzorce projektowe opisaliśmy w dodatku A.
XP — Patrz programowanie ekstremalne.
Zabezpieczenie (security) — Własność systemu oznaczająca jego zdolność do ochrony zaso-
bów przed nieuprawnionym dostępem, zamierzonym lub przypadkowym.
Zachowanie zewnętrzne (external behavior) — Zachowanie systemu z perspektywy jego
użytkownika. Opisywane przez model przypadków użycia.
Zadanie (task) — Niepodzielna jednostka pracy jako przedmiot zarządzania. Realizacja
zadania wiąże się ze zużywaniem zasobów i wytwarzaniem jednego lub więcej produktów.
Zagadnienie (issue) — Krytyczny problem, dla którego nie istnieje oczywiste rozwiązanie.
Zagadnieniowy model cyklu życiowego (issue-based life cycle model) — Ukierunkowany
na encje model cyklu życiowego, w którym model zagadnień wykorzystywany jest do
monitorowania i kontrolowania postępu prac w projekcie.
Zagnieżdżona maszyna stanów (nested state machine) — Maszyna stanów modelująca
zachowanie obiektu w określonym stanie, w postaci zbioru zagnieżdżonych stanów,
przejść wewnętrznych i akcji.
Zagnieżdżony stan (nested state) — Na diagramie stanów — stan zagnieżdżonej maszyny
stanów.
Zamknięte zagadnienie (closed issue) — Zagadnienie, dla którego określono rozstrzygnięcie.
Zapora sieciowa (firewall) — Filtr pakietów, przepuszczający albo odrzucający pakiet na
podstawie zawartego w nim adresu źródłowego i docelowego.
B.1. Terminologia 815

Zarządzanie generowaniem binariów (build management) — Wsparcie narzędziowe dla


automatyzacji generowania binariów systemu w przypadku wprowadzenia zmian w jego
kodzie źródłowym lub innych komponentach.
Zarządzanie konfiguracją (configuration management) — Aktywność, w ramach której
programiści monitorują i kontrolują zmiany wprowadzane do systemu lub jego modeli.
Zasadniczymi celami zarządzania konfiguracją są:
• zbieranie informacji niezbędnej do przywracania wcześniejszych wersji systemu
w razie potrzeby,
• ochrona przed zmianami niespójnymi z celami projektu,
• umożliwienie równoległej realizacji kilku ścieżek rozwojowych i rozwiązywanie
ewentualnych problemów związanych z ich integrowaniem.
Zarządzanie procesami (process management) — W kontekście zarządzania konfiguracją
— zbiór zasad określających tworzenie i dokumentowanie wersji, powiadamianie uczestników
o pojawianiu się nowych wersji i powiadamianie programistów o niepowodzeniach gene-
rowania binariów.
Zarządzanie projektem (project management) — Planowanie, szacowanie budżetu, mo-
nitorowanie i kontrolowanie procesu tworzenia systemu. Wykonujący te aktywności mene-
dżer projektu upewnia się, że spełnione są ograniczenia projektu i jego cele.
Zarządzanie racjonalizacją (rationale management) — Tworzenie, pozyskiwanie, aktualizo-
wanie i wykorzystywanie informacji składającej się na racjonalizację.
Zarządzanie ryzykiem (risk management) — Menedżerska metoda identyfikowania i niwe-
lowania obszarów niepewności, zanim odbije się ona niekorzystnie na harmonogramie lub
jakości systemu.
Zasada zastępowania Liskov (Liskov Substitution Principle) — Formalna definicja dzie-
dziczenia specyfikacyjnego. Zgodnie z tą zasadą, jeżeli w kodzie ldienta występuje wywołanie
metody definiowanej w superklasie, na rzecz obiektu tejże superklasy, to obiekt ten można
zastąpić obiektem dowolnej subklasy bez jakichkolwiek innych zmian w kodzie.
Zasoby (resources) — Środki niezbędne do zrealizowania projektu — czas, sprzęt i praca.
Zbieranie wymagań (requirements elicitation) — Aktywność, w ramach której uczestnicy
projektu definiują przeznaczenie systemu. Efektem zbierania wymagań jest stworzenie
modelu analitycznego.
Zbiór OCL (OCL set) — Kolekcja w języku OCL reprezentująca zbiór elementów (w sensie
teorii mnogości). Zbiory wykorzystywane są w języku OCL do reprezentowania wyników
nawigacji po pojedynczym skojarzeniu.
Zbiór zmian (change set) — Zbiór delt stanowiący różnicę między dwiema konfiguracjami.
Zdarzenie (event) — Wystąpienie określonej sytuacji w systemie. Zdarzenie jest instancją
klasy zdarzeniowej. Przykładami zdarzeń są bodźce generowane przez aktora, upłynięcie
zdefiniowanego odcinka czasu lub przesłanie komunikatu między dwoma obiektami.
Zdarzenie komunikacyjne (communication event) — Akt wymiany informacji o zdefi-
niowanym zakresie, podporządkowanej zdefiniowanemu celowi. Zdarzenia komunikacyjne
mogą być planowane lub występować spontanicznie. Przykładami zdarzeń komunikacyj nych
są przeglądy klienckie, przeglądy statusowe i raportowanie problemów.
816 Dodatek B • Objaśnienia haseł

Zdolność dostępu (capability) — Reprezentacja macierzy kontroli dostępu w formie listy par
(klasa-operacja) stowarzyszonej z aktorem. Każda ze wspomnianych par oznacza uprawnienie
tegoż aktora do wykonywania konkretnej operacji na konkretnej klasie. Materialnymi odpo-
wiednikami zdolności dostępu są: klucz do zamka, karta inteligentna i bilet do teatru.
Zebranie (meeting) — Synchroniczny mechanizm komunikacyjny obejmujący prezentowanie,
dyskutowanie, negocjowanie i rozstrzyganie zagadnień przez uczestników, w formie osobistego
spotkania, telefonicznej rozmowy wielostronnej lub wideokonferencji.
Zebranie statusowe (status meeting) — Planowe zdarzenie komunikacyjne, w ramach którego
kierownik zespołu monitoruje status prac prowadzonych przez ten zespół.
Zebranie uruchamiające projekt (kick-off meeting) —- Zebranie wszystkich uczestników
projektu oznaczające formalny koniec fazy inicjacyjnej projektu i wejście projektu w fazę
ustaloną.
Zespół (team) — Grupa uczestników wspólnie rozwiązujących problem projektowy.
Zespół funkcyjny — To samo, co zespół podsystemu.
Zespół międzyfunkcyjny (cross-functional team) — Zespół odpowiedzialny za wspieranie
zespołu funkcyjnego w wykonywaniu przezeń aktywności międzyfunkcyj nych, takich jak
zarządzanie konfiguracją, integrowanie i testowanie.
Zespół podsystemu (subsystem team) — Zespół odpowiedzialny za opracowanie danego
podsystemu. Przeciwieństwo zespołu międzyfunkcyjnego.
Zjawisko (phenomenon) — Fragment rzeczywistości postrzegany z perspektywy modelowania
jako obiekt. Modelowanie sprowadza się do wyboru zjawisk interesujących w danym kontek-
ście, identyfikowania ich wspólnych cech i abstrahowania tych zjawisk w formie koncepcji.
Złączenie (join node) — Na diagramie aktywności UML — węzeł kontrolny reprezentujący
synchronizację wielu wątków do jednego wątku.
Zmiana (change) — Adaptacja wybranego aspektu systemu, na przykład dodanie lub zmody-
fikowanie wymagania albo zastąpienie jednego komponentu innym. Patrz także delta.
Żądanie wyjaśnień (request for clarification) — Pozaplanowe zdarzenie komunikacyjne,
polegające na żądaniu przez uczestników dodatkowych informacji.
Żądanie zmiany (change request) — Pozaplanowe zdarzenie komunikacyjne, związane
z propozycją uczestnika dotyczącą zmodyfikowania pewnej cechy produktu lub dodania
nowej. W zarządzaniu konfiguracją — formalny raport żądający zmodyfikowania elementu
konfiguracji.
D ^ Clnmnil/ łnrmlnńin
u.z. MowniK terminów angieisKicn

abstract data type - Abstrakcyjny typ danych

action — Akcja

activity — Aktywność

activity partition — Fartycja aKiywnosci

#
application object - Obiekt aplikacyjny

arcnitecturai style — otył arcinieKioniczny


attribute — Atrybut
attribute type - T y p atrybutu

branch — Gałąź

change control — Kontrolowanie zmian


B.2. Słownik terminów angielskich 819

chief programmer organization - Organizacja głównego programisty


clarity Klarowność

class diagram - Diagram klas

CM aggregate — Agregat CM

consistency — Spójność ^
control node - Węzeł kontrolny
control object - Obiekt sterujący

CRC card - Karta CRC


criterion — Kryterium
critical path — Ścieżka krytyczna

datatype- Typ danych

decision Decyzja

derived class - Klasa pochodna


design goal - Cel projektowy
end user criterion — Kryterium użytkownika

entity object — Obiekt encji

error — Błąd

event — Zdarzenie
event class — Klasa zdarzeniowa
event-driven control — Sterowanie oparte na zdarzeniach
exception - Wyjątek

exit condition — Warunek wyjściowy

Jbxtreme rrogratnming — programowanie eKstremaine

failure - Awaria
falsification - Falsyfikacja
fault - Usterka

fault detection — Wykrywanie usterek

firewall — Zapora sieciowa


firewall — Firewall

foreign key — Klucz obcy


822 Dodatek B • Objaśi

fork node — R o z w i d l e n i e

Gantt chart — Wykres Gantta

global access table — Globalna tabela dostępu


goal Cel

hallway conversation — Konwersacje okazjonalne


nierarcnicai aecomposition — ueKompozycja nierarcniczna
hierarchical organization — Organizacja hierarchiczna

horizontal prototype — Prototyp poziomy

include relationship — Relacja zawierania


infrastructure jrameworK — rrameworK lnirastruKiurainy
inheritance — Dziedziczenie
inheritance relationship — Relacja dziedziczenia
interface inheritance — Dziedziczenie interfejsu

internal work product — Produkt wewnętrzny


invariant — Niezmiennik

issue resolution — Kozstrzyganie zagadnienia

Joint Application Design — Połączony projekt aplikacji


Key Process Area— Kluczowy obszar procesu

layer — Warstwa

life cycle Cykl życiowy


link - Łącze

maintenance criterion — Kryterium pielęgnowalności

master directory — Katalog główny


model object - Obiekt modelu

object — Obiekt

OCL bag — Wielozbiór OCL


OCL collection — Kolekcja OCL
OCL sequence - Ciąg OCL
OCL set — Zbiór OCL

one-to-one association - Jeden do jednego


B.2. Słownik terminów angielskich 825

open issue — Otwarte zagadnienie


operation — Operacja
operations requirement — Wymaganie operacyjne
organization — Organizacja
package — Pakiet
packaging requirement — Wymaganie pakietowe
participant — Uczestnik
participating actor — Aktor uczestniczący
participating actor instance — Instancja aktora uczestniczącego
participating object — Obiekt uczestniczący
partition — Partycja
pattern expert — Ekspert wzorca
pattern interface — Interfejs wzorca
peer review — Przegląd partnerski
peer-based communication structure — Komunikacja partnerska

peer-to-peer architectural style — Peer-to-peer

performance — Wydajność
performance criterion — Kryterium wydajnościowe
performance testing — Testowanie wydajnościowe
persistent data — Trwałe dane
PERT chart — P E R T

phase — Faza
phenomenon — Zjawisko
pilot testing — Testowanie pilotażowe
pipe and filter architectural style — Filtry i p o t o k i

portability — Przenośność
postcondition — Warunek końcowy
postmortem review — Post mortem
precondition — Warunek wstępny
primary facilitator — Główny facilitator
primary key — Klucz główny
problem definition — Definiowanie problemu
problem domain — Dziedzina problemowa
problem inspection — Inspekcja problemu
problem presentation — Prezentacja problemu
problem solving — Rozwiązywanie problemu
process group — Grupa procesów
process management — Zarządzanie procesami

project velocity Szybkość projektu

prototyping Prototypowanie
reporting structure — Struktura raportowania
repository — Repozytorium
repository architectural style — Architektura repozytoryjna

responsivity — Reaktywność

reviewer — Weryfikator
risk — Ryzyko

role - Roia

security — Zabezpieczenie
828 Dodatek B • Objaśnienia haseł

service — Usługa

slack time — Czas przestoju


software architecture — Architektura oprogramowania

software library — Biblioteka oprogramowania


software life cycle — Cykl życiowy oprogramowania
software life cycle model — Model cyklu życiowego oprogramowania

software reliability — Niezawodność oprogramowania


solution domain Dziedzina realizacyjna
solution domain specialist — Specjalista z dziedziny realizacyjnej
solution object — Obiekt realizacyjny
source code — K o d ź r ó d ł o w y

specification innentance — U'ZieQ.ziczenie specyTjjvacyine

status accounting — Raportowanie statusu

stereotype — Stereotyp

strict inheritance — Ścisłe dziedziczenie

structural testing —- Testowanie strukturalne

subsystem decom-position — D e k o m p o z y c j a systemu


subsystem interface — Interfejs podsystemu
supportability — Wspieralność
swirnlane — Tor pływacką

system arcnitect — ArcniteKt systemu

task - Zadanie
task model- Model zadań

technical writer — D o k u m e n t a l i s t a

thread - Wątek

three-tier architectural style — Architektura trójwarstwowa


top-down testing — Testowanie zstępujące
top-level design - Wysokopoziomowy projekt systemu
traceability — Identyfikowalność
training scenario — Scenariusz treningowy

transition — Frzejscie
tupie — Krotka
type- Typ

vertical prototype — Prototyp pionowy

Work Breakdown Structure — Struktura podziału pracy


work package — Pakiet pracy
work product — Produkt
workflow — Przepływ pracy
DODATEK C

Bibliografia

[Abbott, 1983] R. Abbott Program design by informal English descriptions,


„Communications of the ACM", t. 26, nr 11,1983.
[Adams, 2000] D. N. Adams Mostly Harmless, Ballantine Books, 2000.
[AgileManifesto, 2001] h ttp://www. agilemanifesto. org.
[Aguanno, 2005] K. Aguanno Managing Agile Projects, Multi-Media
Publications, Ontario, 2005.
[Albrecht i Gaffney, 1983] A. J. Albrecht, J. E. Gaffney Jr. Software function, source lines
of code, and development effort prediction: A software science
validation, „IEEE Transactions on Software Engineering",
t. SE-9, nr 6, listopad 1983.
[Allen, 1995] T. J. Allen Managing the Flow of Technology: Technology
Transfer and the Dissemination of Technological Information
within the R&D Organization, wyd. drugie, MIT Press,
Cambridge, MA, 1995.
[Ambler, 1998] S. W. Ambler Process Patterns: Building Large-Scale Systems
Using Object Technology, Cambridge University Press,
New York, 1998.
[Ambler, 2002] S. Ambler Agile Modeling: Effective Practices for Extreme
Programming and the Unified Process, John Wiley & Sons, 2002.
[Apache] Apache http://www.apache.org/.
[Babich, 1986] W. A. Babich Software Configuration Management,
Addison-Wesley, Reading, MA, 1986.
[Baker i in., 2008] P. Baker, Z. R. Dai, R. Grabowski Model-Driven Testing:
Using the UML Testing Profile, Springer, Berlin, 2008.
[Barone i Switzer, 1995] J. T. T. Barone, J. Switzer Interviewing: Art and Skill,
Allyn & Bacon, 1995.
[Bass i in., 2003] L. Bass, P. Clements, R. Kazman Software Architecture in Practice,
wyd. drugie, Addison-Wesley, Reading, MA, 2003.
[BEA] BEA WebLogic Platform,
http://www.bea.com/products/weblogic/platform.
832 Dodatek C • Bibliografia

[Beck i Andres, 2005] K. Beck, C. Andres Extreme Programming Explained: Embrace


Change, wyd. drugie, Addison-Wesley, Reading, MA, 2005.
[Beck i Cunningham, 1989] K. Beck, W. C u n n i n g h a m „A laboratory for teaching
object-oriented thinking" OOPSLA'89 Conference
Proceedings, New Orleans, LA, 1 - 6 października 1989.

[Berliner, 1990] B. Berliner „CVS II: Parallelizing software development",


Proceedings of the 1990 USENIX Conference, Washington, DC,
str. 22 - 26, styczeń 1990.
[Bersoffiin., 1980] E. H. Bersoff, V. D. H e n d e r s o n , S. G. Siegel Software
Configuration Management: An Investment in Product
Integrity, Prentice Hall, Englewood Cliffs, NJ, 1980.
[Binder, 2000] R. V. Binder Testing Object-Oriented Systems: Models, Patterns,
and Tools, Addison-Wesley, Reading, MA, 2000.
[Birrer, 1993] E. T. Birrer „Frameworks in the financial engineering domain:
An experience report", ECOOP'93 Proceedings, Lecture Notes
in Computer Science, n r 707, 1993.
[Błaha i Premerlani, 1998] M. Blaha, W. Premerlani, Object-Oriented Modeling and Design
for Database Applications, Prentice Hall, Upper Saddle River,
NJ, 1998.
[ B o e h m i i n . , 1998] B. Boehm, A. Egyed, J. Kwan, D. Port, A. Shah, R. Madachy
Using the WinWin spiral model: A case study, „IEEE Computer"
31(7), str. 33 - 4 4 , 1 9 9 8 .
[Boehm i in., 2000] B. Boehm, E. Horowitz, R. Madachy, D. Reifer, B. K. Clark,
B. Steece, A. W. Brown, S. Chulani, C. Abts Software Cost
Estimation with COCOMOII, Prentice Hall, Upper Saddle
River, NJ, 2000.
[Boehm, 1987] B. Boehm A spiral model of software development and
enhancement, „Software Engineering Project Management",
str. 128 - 142,1987.
[Boehm, 1991] B. Boehm Software risk management: Principles and practices,
„IEEE Software", t.l, str. 32 - 41, 1991.
[Bonatti, 2001] W. Bonatti The Mountains of My Life, Modern Library,
Random House, New York, 2001.
[Booch i in., 2005] G. Booch, J. Rumbaugh, I. Jacobson The Unified Modeling
Language User Guide, Addison-Wesley, Reading, MA, 2005.
[Booch, 1994] G. Booch Object-Oriented Analysis and Design with Applications,
wyd. drugie, Benjamin/Cummings, Redwood City, CA, 1994.
U. W. Borghoff, J. Schlichter Computer Supported Cooperative
[Borghoff i Schlichter, 2000] Work: An Introduction into Distributed Applications,
Springer-Verlag. 2000.
A. Borning The programming language aspects of ThingLab,
[Borning, 1981] a constraint-oriented simulation laboratory, w „ACM TOPLAS"
3 (4), październik 1981.
Bibliografia 833

[Brooks, 1995] F. P. Brooks The Mythical Man Month: Anniversary Edition:


Essays on Software Engineering, wyd. Addison-Wesley,
Reading, MA, 1995.
[Brown i in., 1999] W. J. Brown, H. W. McCormick, S. W. Thomas AntiPatterns
and Patterns in Software Configuration Management, Wiley,
New York, 1999.

[Bruegge, 1992] B. Bruegge „Teaching an industry-oriented software


engineering course", Software Engineering Education, SEI
Conference, Lecture Notes in C o m p u t e r Sciences, t. 640,
str. 65 - 87, Springer-Verlag, październik 1992.

[Bruegge, 1994] B. Bruegge „From toy systems to real system development",


Improvements in Software Engineering Education, W o r k s h o p
of the German Chapter of the ACM, B.G. Teubner Verlag,
Stuttgart, str. 62-72, luty 1994.
[Bruegge i Coyne, 1993] B. Bruegge, R. Coyne Model-based software engineering in
larger scale project courses, „IFIP Transactions on Computer
Science and Technology", t. A-40, str. 273 - 287, Elsevier
Science, Netherlands, 1993.
[Bruegge i Coyne, 1994] B. Bruegge, R. Coyne „Teaching iterative object-oriented
development: Lessons and directions", w: J. L. Diaz-Herrera
(red.) 7th Conference on Software Engineering Education,
Lecture Notes in Computer Science, t. 750, str. 413 - 427,
Springer-Verlag, styczeń 1994.

[Bruegge i in., 1992] B. Bruegge, J. Blythe, J. Jackson, J. Shufelt „Object-oriented


system modeling with OMT", Conference Proceedings
OOPSLA y92 (Object-Oriented Programming Systems,
Languages, and Applications), str. 359 - 376, październik
1992.
[Bruegge i in., 1993] B. Bruegge, T. Gottschalk, B. Luo „A framework for dynamic
program analyzers", OOPSLA'93, (Object-OrientedProgramming
Systems, Languages, and Applications), Washington DC,
str. 65 - 82, wrzesień 1993.
[Bruegge i in., 1994] B. Bruegge, K. O'Toole, D. Rothenberger „Design
considerations for an accident management system", w: M.
Brodie, M. Jarke, M. Papazoglou (red.) Proceedings of the
Second International Conference on Cooperative Information
Systems, str. 90 - 100, University of Toronto Press, Toronto,
Canada, maj 1994.

[BSCW] Basic Support for Cooperative Work, http://bscw.gmd.de.


[Buckingham Shum i Hammond, 1994] S. Buckingham Shum, N. H a m m o n d Argumentation-based
design rationale: What use at what cost?, „International
Journal of H u m a n - C o m p u t e r Studies", t. 40, str. 603-652,
1994.

[Buhl, 1956] H. Buhl Nanga Parbat Pilgrimage, H o d d e r & Stoughton,


London, 1956.
834 Dodatek C • Bibliografia

[Buschmann i in., 1996] F. Buschmann, R. Meunier, H. Rohnert, P. Sommerlad, M.


Stal Pattern-Oriented Software Architecture: A System of
Patterns, John Wiley & Sons, Chichester, U.K., 1996.
[Campbell i Islam, 1993] R. H. Campbell, N. Islam A technique for documenting the
framework of an object-oriented system, „Computing Systems",
6, str. 363 - 389,1993.
[ C a r r i i n . , 1993] M. J. Carr, S. L. Konda, I. Monarch, F. C. Ulrich, C. F. Walker
Taxonomy-Based Risk Identification, Technical Report
CMU/SEI-93-TR-6, Software Engineering Institute,
Carnegie Mellon University, Pittsburgh, PA, 1993.

[Carroll, 1995] J. M. Carroll (red.) Scenario-Based Design: Envisioning Work


and Technology in System Development, Wiley, New York, 1995.
[Charette, 1989] R. N. Charette Software Engineering Risk Analysis and
Management, McGraw-Hill, New York, 1989.
[Chung i in., 1999] L. Chung, B. A. Nixon, E. Yu, J. Mylopoulos Non-Functional
Requirements in Software Engineering, Kluwer Academic,
Boston, 1999.
[Clements i in., 2002] P. Clements, R. Kazam, M. Klein Evaluating Software
Architectures: Methods and Case Studies, SEI Series in Software
Engineering, Addison-Wesley, 2002.
[ C o a d i i n . , 1995] P. Coad, D. North, M. Mayfield Object Models: Strategies,
Patterns, & Applications, Prentice Hall, Englewood Cliffs,
NJ, 1995.
[Cockburn, 2001a] A. Cockburn Agile Software Development, Addison-Wesley,
Reading, MA, 2001.
[Cockburn, 2001b] A. Cockburn Writing Effective Use Cases, Addison-Wesley,
Reading, MA, 2001.
[Cohn, 2006] M. Cohn Agile Estimating and Planning, Pearson, Upper
Saddle River, NJ, 2006.
[Conklin i Burgess-Yakemovic, 1991] J. Conklin, K. C. Burgess-Yakemovic A process-oriented
approach to design rationale, „Human-Computer Interaction",
t. 6, str. 357 - 391, 1991.
[Constantine i Lockwood, 1999] L. L. Constantine, L. A. D. Lockwood Software for Use,
Addison-Wesley, Reading, MA, 1999.
[Constantine i Lockwood, 2001] L. L. Constantine, L. A. D. Lockwood „Structure and style
in use cases for user interface design", w: M. van Harmelen
(red.) Object-Oriented User Interface Design, 2001.
[Contract4J] http://www. contract4j. org/.
[Coyne i in., 1995] R. Coyne, B. Bruegge, A. Dutoit, D. Rothenberger „Teaching
m o r e comprehensive model-based software engineering:
Experience with Objectory's use case approach", w: L. Ibraham
(red.), 8th Conference on Software Engineering Education,
Lecture Notes in C o m p u t e r Science, str. 339 - 374,
Springer-Verlag, kwiecień 1995.

[CruiseControl] http:/I cruisecontrol.sou reef o rge.ne t/.


Bibliografia 835

[Curtis i in., 1988] B. Curtis, H. Krasner, N. Iscoe Afield study of the software
design process for large systems, „Communications of the
ACM", t. 31, n r 11, str. 1268 - 1287, 1988.

[Cusumano i Selby, 1997] M. A. Cusumano, R. W. Selby How Microsoft Builds Software,


„Communications of the ACM", t. 40, nr 6, str. 5 3 - 6 1 ,
1997.
[D'Souza i Wills, 1999] D. F. D'Souza, A. C. Wills Objects, Components, and
Frameworks with UML: The Catalysis Approach,
Addison-Wesley, Reading, MA, 1999.

[Dart, 1991] S. Dart „Concepts in configuration management systems",


Third International Software Configuration Management
Workshop, ACM, czerwiec 1991.
[Date, 2004] C. J. Date An Introduction to Database Systems, wyd. ósme,
Addison-Wesley, Reading, MA, 2004.

[Day i Zimmermann, 1983] J. D. Day, H. Z i m m e r m a n n „The OSI Reference Model",


Proceedings of the IEEE, t. 71, str. 1334 - 1340, grudzień 1983.

[De Marco, 1978] T. De Marco Structured Analysis and System Specification,


Yourdon, New York, 1978.

[Deming, 1982] E. W. Deming Out of the Crisis, MIT Press, Cambridge,


MA, 1982. wyd. drugie 2000.

[Dijkstra, 1968] E. W . Dijkstra The Structure of the „T.H.E"


Multiprogramming System, „Communication of the A C M "
18 (8), str. 4 5 3 - 4 5 7 , 1968.
[Dijkstra, 1976] E. W. Dijkstra A Discipline of Programming, Prentice Hall,
Englewood Cliffs, NJ, 1976.
[DoD-STD-2167A] DoD-STD-2167A, Military Standard, Defense Systems Software
Development, US D e p a r t m e n t of Defense, W a s h i n g t o n ,
DC, 1988.
[Douglass, 1999] B. P. Douglass Doing Hard Time: Using Object Oriented
Programming and Software Patterns in Real Time Applications,
Addison-Wesley, Reading, MA, 1999.
[Doyle i Straus, 1982] M. Doyle, D. Straus How to make meetings work, The Berkeley
Publishing Group, New York, NY, 1982.
[Dumas i Redish, 1998] J. S. Dumas, J. C. Redish A Practical Guide to Usability Testing,
Ablex, NJ, 1993.
[Dutoit i Bruegge, 1998] A. H. Dutoit, B. Bruegge, Communication metrics for software
development, „IEEE Transactions on Software Engineering",
sierpień 1998.
A. H. Dutoit, B. Bruegge, R. F. Coyne „The use of an issue-
[ D u t o i t i i n . , 1996]
based model in a team-based software engineering course",
Conference Proceedings of Software Engineering: Education
and Practice (SEEP'96), Dunedin, NZ, styczeń 1996

A. H. Dutoit, R. McCall, I. Mistrik, B. Paech (red.) Rationale


[Dutoit i in., 2006]
Management in Software Engineering, Springer, Heidelberg,
2006.
836 Dodatek C • Bibliografia

[Dutoit i Paech, 2001] A. H. Dutoit, B. Paech „Rationale management in software


engineering" w: S.K. C h a n g (red.) Handbook of Software
Engineering and Knowledge Engineering, t. 1, World Scientific
Publishing, 2001.
[Dutoit i Paech, 2002] A. H. Dutoit, B. Paech, Rationale-based use case specification,
„Requirements Engineering Journal", 7(1), str. 3 - 19, 2002.

[Duval i in., 2007] P. Duval, S. Matyas, A. Glover Continuous Integration: Improving


Software Quality and Reducing Risk, Addison-Wesley,
Reading, MA, 2007.
[Elssamadisy, 2009] A. Elssamadisy Agile Adoption Patterns, Addison-Wesley,
Reading, MA, 2009.
[Erl, 2005] T. Erl Service-Oriented Architectures:Concepts, Technology,
and Design, Prentice Hall, Upper Saddle River, NJ, 2005.
[ E r m a n i i n . , 1980] L. D. Erman, F. Hayes-Roth i in. The Hearsay-II
Speech-Understanding System: Integrating knowledge
to resolve uncertainty, „ACM Computing Surveys", t. 12,
nr 2, str. 213 - 253,1980.
[Fagan, 1976] M. E. Fagan Design and code inspections to reduce errors
in program development, „IBM System Journal", t. 15, n r 3,
str. 1 8 2 - 2 1 1 , 1976.
[Fayad i H a m u , 1997] M. E. Fayad, D. S. H a m u Object-oriented enterprise
frameworks: Make vs. buy decisions and guidelines for
selection, „The Communications of ACM", 1997.
[Feiler i Tichy, 1998] P. Feiler, W . Tichy „Propagator: A family of patterns",
Proceedings of TOOLS-23'97, 28 lipca - 1 sierpnia 1997,
Santa Barbara, CA.
[Feynman, 1988] R. P. Feynman „Personal observations on the reliability of the
Shuttle", Rogers Commission The Presidential Commission
on the Space Shuttle Challenger Accident Report, Washington,
DC, czerwiec 1986. Artykuł dostępny także w internecie, między
innymi pod adresem http://www.virtualschool.edu/mon/
SocialConstructionlEeynmanChallengerRpt.html.

[Fisher i in., 1991] R. Fisher, W. Ury, B. Patton Getting to Yes: Negotiating


Agreement Without Giving In, wyd. drugie, Penguin Books,
New York, 1991. Wydanie polskie R. Fisher, W. Ury, B. Patton
Dochodząc do TAK. Negocjowanie bez poddawania się,
Polskie Wydawnictwo Ekonomiczne, Warszawa, 2000.
[Floyd, 1967] R. W. Floyd „Assigning meanings to programs", w Proceedings
of the American Mathematics Society Symposium in Applied
Mathematics, t. 19, str. 1 9 - 3 1 , 1967.
[Fowler, 1997] M. Fowler Analysis Patterns: Reusable Object Models,
Addison-Wesley, Reading, MA, 1997.
[Fowler, 2000] M. Fowler Refactoring: Improving The Design of Existing
Code, Addison-Wesley, Reading, MA, 2000.
Bibliografia 837

[Fowler, 2003] M. Fowler UML Distilled: A Brief Guide To The Standard


Object Modeling Language, wyd. trzecie, Addison-Wesley,
Reading, MA, 2003.
[Fowler, 2005] M. Fowler The new methodology,
http://www.martinfowler.com/articles/newMethodology.html,
zrewidowane w 2005.

[Freeman-Benson, 1990] B. Freeman-Benson Kaleidoscope: Mixing objects,


constraints, and imperative programming, w
„OOPSLA/SIGPLAN Notices" 25 (10), str. 77 - 88,
październik 1990.

[FRIEND, 1994] FRIEND Project Documentation, School of Computer


Science, Carnegie Mellon University, Pittsburgh, PA, 1994.

[Gamma i in., 1994] E. Gamma, R. Helm, R. Johnson, J. Vlissides Design Patterns:


Elements of Reusable Object-Oriented Software, Addison-Wesley,
Reading, MA, 1994. Wydanie polskie Wzorce projektowe.
Elementy oprogramowania obiektowego wielokrotnego użytku,
Helion 2010.

[Gantt, 1910] H. L. Gantt Work, wages, and profits, „The Engineering


Magazine", New York, 1910.
[Gladwin, 1964] T. Gladwin „Culture and logical process" w W. Goodenough
(red.) Explorations in Cultural Anthropology: Essays Presented
to George Peter Mur dock, McGraw-Hill, New York, 1964.

[Gleick, 1987] J. Gleick Chaos — Making a New Science, Penguin Books Ltd,
Harmondsworth, Middlesex, 1987.

[Goldberg i Kay, 1976] A. Goldberg, A. Kay Smalltalk-72 Instruction Manual,


Xerox Palo Alto, CA, 1976.
[Grady, 1992] R. Grady Practical Software Metrics for Project Management
and Process Improvement, Prentice Hall, Englewood Cliffs,
NJ, 1992.
[Grenning, 2002] J. Grenning Planning Poker, http://www.planningpoker.com/,
2002.
[Grudin, 1988] J. Grudin „Why CSCW applications fail: Problems in design
and evaluation of organization interfaces", Proceedings of
CSCW'88, Portland, OR, 1988.
[Grudin, 1990] J. Grudin „Obstacles to user involvement in interface design
in large product development organizations", Proceedings
of IFIPINTERACT90 Third International Conference on
Human-Computer Interaction, Cambridge, U.K., sierpień 1990.

[Halstead, 1977] M. H. Halstead Elements of Software Science, Elsevier,


New York, 1977.
[Hammer i Champy, 1993] M. Hammer, J. C h a m p y Reengineering The Corporation:
a Manifesto For Business Revolution, Harper Business,
New York, 1993.

[Harel, 1987] D. Harel Statecharts: A visual formalism for complex systems,


„Science of Computer Programming", str. 231 - 274,1987.
838 Dodatek C • Bibliografia

[Hartkopf i in., 1997] V. Hartkopf, V. Loftness, A. Mahdavi, S. Lee, J. Shankavarm


An integrated approach to design and engineering of intelligent
buildings — The Intelligent Workplace at Carnegie Mellon
University, „Automation in Construction", t. 6, str. 4 0 1 - 4 1 5 ,
1997.
[Herbert, 1985] F. Herbert Chapterhouse: Dune, Orion Publishing, 1985.
[Highsmith i Orr, 2000] J. Highsmith, K. O r r Adaptive Software Development:
A Collaborative Approach to Managing Complex Systems,
Dorset House, 2000.
[Highsmith, 2004] J. Highsmith Agile Project Management, Pearson Education,
Boston, 2004.
[Hoare, 1969] C. A. R. Hoare An axiomatic basis for computer
programming, „Communications of the ACM", t. 20, nr 6,
str. 576 - 580, październik 1969.
[Hoare, 1980] C. A. R. Hoare „The emperor's old clothes", Turing Award
Lecture, 1980. Artykuł dostępny także p o d adresem
http://www.es. ucsb.edu/-ravenben/papers/ coreos/Hoa%l .pdf
[Hock, 1999] D. Hock Birth of the ChaordicAge, Berrett-Koehler Publishers,
San Francisco, 1999.
[Hofmeister, 2000] C. Hofmeister, R. Nord, D. Soni Applied Software Architecture,
Object Technology Series, Addison-Wesley, Reading, MA, 2000.
[Hopper, 1981] G. M. H o p p e r The First Bug, „Annals of the History of
Computing 3", str. 285 - 286,1981.
[Horn, 1992] B. H o r n „Constraint patterns as a basis for object-oriented
programming", Proceedings of the OOPSLA'92, Vancouver,
Canada, 1992.
[Humphrey, 1989] W. Humphrey Managing the Software Process, Addison-Wesley,
Reading, MA, 1989.
[IBM] IBM WebSphere Software Platform for E-Business,
http://www.ibm.com/websphere/.
[IEEE Std. 1042-1987] IEEE Guide to Software Configuration Management,
IEEE Standards Board, wrzesień 1987.
[IEEE Std. 1058-1998] IEEE Standard for Software Project Management Plans,
IEEE Computer Society, New York, 1998.
[IEEE Std. 1074-2006] IEEE Standard for Developing Software Life Cycle Processes,
IEEE Computer Society, New York, 2006.
[IEEE Std. 610.12-1990] IEEE Standard Computer Dictionary: A Compilation of IEEE
Standard Computer Glossaries, New York, NY, 1990.
[IEEE Std. 828-2005] IEEE Standard for Software Configuration Management Plans,
IEEE Standards Board, sierpień 2005.
[IEEE Std. 829-2008] IEEE Standard for Software Test Documentation, IEEE Standards
Board, lipiec 2008.
[IEEE Std. 830-1998] IEEE Standard for Software Requirements Specification,
IEEE Standards Board, 1998.
Bibliografia 839

IEEE Std. 982.2-1988] IEEE Guide for the Use of IEEE Standard Dictionary of Measures
to Produce Reliable Software, IEEE Standards Board,
czerwiec 1988.
IEEE/EI A, 1996] IEEE/EIA 12207.0-1996, Industry Implementation of
International Standard ISO/IEC 12207:1995 Standard for
Information Technology — Software life cycle processes,
IEEE Computer Society & Electronic Industries Association,
marzec 1998.

ISO Std. 9126] International Standards Organization, Software engineering


— Product quality. ISO/IEC-9126, Geneva, Switzerland, 2001.
ISO/IEC 12207, 1995] ISO/IEC 12207, Information technology — Software life cycle
processes, I n t e r n a t i o n a l Organization for Standardization
& International Electrotechnical Commission, sierpień 1995.
Jackson, 1995] M. Jackson Software Requirements & Specifications: A Lexicon
of Practice, Principles and Prejudices, Addison-Wesley,
Reading, MA, 1995.
Jacobson i in., 1992] I. Jacobson, M. Christerson, P. Jonsson, G. Overgaard
Object-Oriented Software Engineering — A Use Case Driven
Approach, Addison-Wesley, Reading, MA, 1992.
Jacobson i in., 1995] I. Jacobson, M. Ericsson, A. Jacobson The Object Advantage:
Business Process Reengineering with Object Technology,
Addison-Wesley, Reading, MA, 1995.
Jacobson i in., 1999] I. Jacobson, G. Booch, J. Rumbaugh The Unified Software
Development Process, Addison-Wesley, Reading, MA, 1999.

Jarkę, 1998] M. Jarke Requirements tracing, „Communications of the


ACM", t. 41, N r 12, grudzień 1998.

Javadoc, 2009a] Sun Microsystems, strona główna Javadoc


h ttp:l/java.sun. com/j2se/javadoc/.

Javadoc, 2009b] Sun Microsystems, How to write doc comments for Javadoc,
http://java.sun.com/j2se/javadoc/writingdoccomments/.

JavaEE, 2009] lava Platform, Enterprise Edition, Javasoft 2009,


http://java.sun. com/.
j Contractor] h ttp://j contractor, sourceforge. net/.
JDBC, 2009] JDBC™ — Connecting Java and Databases, JDK
Documentation, Javasoft, 2009.
Jensen i Tonies, 1979] R. W. Jensen, C. C. Tonies Software Engineering, Prentice
Hall, Englewood Cliffs, NJ, 1979.
JFC, 2009] Java Foundation Classes, JDK Documentation, Javasoft, 2009.
Johnson, 1992] P. Johnson Human Computer Interaction: Psychology, Task
Analysis and Software Engineering, McGraw-Hill International,
London, 1992.

[Johnson i Foote, 1988] R. Johnson, B. Foote Designing reusable classes, „Journal of


Object-Oriented Programming", t. 1, nr 5, str. 22 - 35, 1988.
840 Dodatek C • Bibliografia

[Jones, 1977] T. C. Jones „Programmer quality and programmer productivity",


IBM TR-02.764, 1977.
[JUnit, 2009] JUnit, http://www.junit.org/.
[Katzenbach i Smith, 1994] J. R. Katzenbach, D. K. Smith The Wisdom of Teams: Creating
The High-Performance Organization, Harper Business, 1994.
[Kayser, 1990] T. A. Kayser Mining Group Gold, Serif, El Segundo, CA, 1990.
[Kelly, 1984] J. F. Kelly An iterative design methodology for user-friendly
natural language office information applications, „ACM
Transactions on Information Systems", t. 2, nr 1, styczeń 1984.
[Kemerer, 1997] C. F. Kemerer „Case 7: Microsoft Corporation: Office Business
Unit", Software Project Management: Readings and Cases,
Irwin/McGraw-Hill, Boston, MA, 1997.
[Knuth, 1986] D. E. Knuth The TeXbook, Addison-Wesley, Reading, MA, 1986.
G. Kotonya, I. Sommerville Requirements Engineering with
[Kotonya & Sommerville, 1996]
Viewpoints, „Software Engineering Journal" 11(1), 1996.
R. Kramer „iContract — The Java design by contract tool",
[Kramer, 1998]
Technology of Object-Oriented Languages and Systems,
IEEE Computer Society Press, str. 295,1998.
[Kraut i Streeter, 1995] R. E. Kraut, L. A. Streeter Coordination in software development,
„Communications of the ACM", t. 38, nr 3, marzec 1995.
[Kruchten, 1998] P. Kruchten The Rational Unified Process: An Introduction,
Addison-Wesley, Reading, MA, 1998.
[Kunz i Rittel, 1970] W. Kunz, H. Rittel „Issues as elements of information systems",
Working Paper Nr 131, Institut fur Grundlagen der Plannung,
Universitat Stuttgart, Germany, 1970.
[Larman i Vodde, 2008] C. Larman, B. Vodde Scaling Lean & Agile Development:
Thinking and Organizational Tools for Large-Scale Scrum,
Addison-Wesley, Reading, MA, 2008.
[Larman, 2005] C. L a r m a n Applying UML and Patterns: An Introduction
to Object-Oriented Analysis and Design, wyd. trzecie, Prentice
Hall, Upper Saddle River, NJ, 2005.
[Leblang, 1994] D. Leblang „The CM challenge: Configuration management
that works", w: W.F. Tichy (red.), Configuration Management,
t. 2 Trends in Software, Wiley, New York, 1994.
[Lee, 1990] J. Lee „A qualitative decision management system" w: P.H.
W i n s t o n , S. Shellard (red.) Artificial Intelligence at MIT:
Expanding Frontiers, 1.1, str. 104 - 133, MIT Press, Cambridge,
MA, 1990.
[Lee, 1997] J. Lee Design rationale systems: Understanding the issues,
„IEEE Expert", maj, czerwiec 1997.
[Leveson, 1995] N. G. Leveson Safeware: System Safety And Computers,
Addison-Wesley, Reading, MA, 1995.
Bibliografia 841

[Lions, 1996] J. L. Lions ARIANE 5 Flight 501 Failure: Report by the Inquiry
Board, http:/'/www.esrin.esa.it/htdocs/tidc/Press/Press96/
ariane5rep.html, 1996.
[Liskov i Guttag, 1986] B. Liskov, J. Guttag Abstraction and Specification in Program
Development, McGraw-Hill, New York, 1986.
[Liskov, 1988] B. Liskov Data abstraction and hierarchy, „SIGPLAN
Notices", t. 23, nr 3, m a j 1988.
[Lotus] Lotus http://www.lotus.com/.
L. Macaulay Requirements Engineering, Springer-Verlag,
[Macaulay, 1996] London, 1996.
A. MacLean, R. M. Young, V. Bellotti, T. Moran Questions,
[MacLean i in., 1991] options, and criteria: Elements of design space analysis,
„ H u m a n - C o m p u t e r Interaction", t.6, str. 201 - 250,1991.

[Marshall, 2001] R. Marshall What really happened at K2? rozdział 24.


w [Bonatti, 2001].
[Martin i Odell, 1992] J. Martin, J. J. Odell Object-Oriented Analysis and Design,
Prentice Hall, Englewood Cliffs, NJ, 1992.

[Mayhew, 1999] D. J. Mayhew The Usability Engineering Lifecycle: A Practitioners


Handbook for User Interface Design, Morgan Kaufmann, 1999.

[McCabe, 1976] T. McCabe A software complexity measure, „IEEE Transactions


on Software Engineering", t.2, nr 12, grudzień 1976.

[Mellor i Shlaer, 1998] S. Mellor, S. Shlaer Recursive Design Approach, Prentice Hall,
Upper Saddle River, NJ, 1998.
[Meyer, 1997] B. Meyer Object-Oriented Software Construction, wyd. drugie,
Prentice Hall, Upper Saddle River, NJ, 1997.
[Microsoft] Microsoft http://www.microsoft.com/.
[MIL Std. 480] MIL Std. 480, U.S. Department of Defense, Washington, DC.

[Miller, 1956] G. A. Miller The magical number seven, plus or minus two:
Some limits on our capacity for processing information,
„Psychological Review", t. 63, str. 81 - 97, 1956.
[Minsky, 1975] M. Minsky „A f r a m e w o r k for representing knowledge,"
w P. Winston (red.) The Psychology of Computer Vision,
McGraw-Hill, 1975.
[Moran i Carroll, 1996] T. P. Moran, J. M. Carroll (red.) Design Rationale: Concepts,
Techniques, and Use, Lawrence Erlbaum Associates, Mahwah,
NJ, 1996.

[Mowbray i Malveau, 1997] T. J. Mowbray, R. C. Malveau CORBA Design Patterns, Wiley,


New York, 1997
[Mozilla] http:// www. mozilla. org/.
[Myers, 1979] G. J. Myers The Art of Software Testing, Wiley, New York, 1979.
Wydanie polskie: G. J. Myers, C. Sandler, T. Badgett, T.M.
T h o m a s Sztuka testowania oprogramowania wyd. Helion
2005, http://helion.pl/ksiazki/artteo.htm.
842 Dodatek C • Bibliografia

[Neumann, 1995] P. G. N e u m a n n Computer-Related Risks, Addison-Wesley,


Reading, MA, 1995.
[Nielsen i Mack, 1994] J. Nielsen, R. L. Mack (red.) Usability Inspection Methods,
Wiley, New York, 1994.
[Nielsen, 1993] J. Nielsen Usability Engineering, Academic, New York, 1993.
[Norman, 2002] D. A. N o r m a n The Design of Everyday Things, Basic Books,
New York, 2002.
[OMG, 2005] Object Management Group, UML Testing Profile Version
1.0. http://www.omg.org/ 2005.
[OMG, 2006] Object Management Group, Object Constraint Language
OMG Available Specification Version 2.0,
http://www. omg. org, 2006.
[OMG, 2008] Object M a n a g e m e n t Group, Common Object Request
Broker Architecture (CORBA) Specification: Version 3.1,
http://www.omg.org, 2008.
[OMG, 2009] Object Management Group, OMG Unified Modeling Language
Superstructure. Version 2.2, http://www.omg.org.
[OWL, 1996] OWL Project Documentation, School of Computer Science,
Carnegie Mellon University, Pittsburgh, PA, 1996.
[Palmer i Felsing, 2002] S. Palmer, J. Felsing A Practical Guide to Feature-Driven
Development, Prentice Hall, Upper Saddle River, NJ, 2002.
[Parnas i Weiss, 1985] D. L. Parnas, D. M. Weiss „Active design reviews: principles
and practice", Proceedings of the Eighth International Conference
on Software Engineering, London, U.K., str. 132 - 136,
sierpień 1985.

[Parnas, 1972] D. Parnas On the Criteria to Be Used in Decomposing Systems


into Modules, „ C o m m u n i c a t i o n s of the ACM", 15 (12),
str. 1053 - 1058, 1972.
[Partsch, 1990] H. Partsch Specification and Transformation of Programs,
Springer-Verlag, 1990.
[Paulish, 2001] D. J. Paulish Architecture-Centric Software Project Management:
A Practical Guide, SEI Series in Software Engineering,
Addison-Wesley, Reading, MA, 2001.
[Paulk i in., 1995] M. C. Paulk, C V . Weber, B. Curtis (red.) The Capability
Maturity Model: Guidelines for Improving the Software Process,
Addison-Wesley, Reading, MA, 1995.
[Perforce] h ttp://www. perforce. com/.
[Perlis, 1982] A. Perlis Epigrams in Programming, „ACM, SIGPLAN", 1982.
[Petroski, 1992] H. Petroski To Engineer is Human, Vintage Books, Random
House, New York, 1992.
[Petroski, 1994] H. Petroski The Evolution of Useful Things, Vintage Books,
R a n d o m House, New York, 1994.
Bibliografia 843

[Pfleeger, 1991] S. L'. Pfleeger Software Engineering: The Production


of Quality Software, wyd. drugie, Macmillan, 1991.
[Pirsig, 1984] R. M. Pirsig Zen and the Art of Motorcycle Maintenance, Bantam
Books, 1984. W y d a n i a polskie: Zen i sztuka oporządzania
motocykla, Rebis 1994; Zen i sztuka obsługi motocykla, Rebis,
2010. [Pirsig, 1991] R. M. Pirsig Lila: An Inquiry into Morals,
Bantam Doubleday Dell, 1991.

[Popper, 1992] K. Popper Objective Knowledge: An Evolutionary Approach,


Clarendon, Oxford, 1992.

[Porter i in., 1997] A. A. Porter, H. Siy, C. A. Toman, L. G. Votta An experiment


to assess the costbenefits of code inspections in large scale software
development, „IEEE Transactions on Software Engineering",
t. 23, nr 6, str. 329 - 346, czerwiec 1997.

[Portny, 2001] S. E. Portny Project Management for Dummies, John Wiley


& Sons, 2000.
[POSIX, 1990] Portable Operating System Interface for Computing
Environments, w: IEEE Std. 1003.1, 1990.
[Potts, 1996] C. Potts „Supporting software design: Integrating design
methods and design rationale", w: T. P. Moran, J. M. Carroll
(red.) Design Rationale: Concepts, Techniques, and Use,
Lawrence Erlbaum Associates, Mahwah, NJ, 1996.
[Potts i Bruns, 1988] C. Potts, G. Bruns „Recording the Reasons for Design Decisions",
w: Proceedings of the 10th International Conference on Software
Engineering, str. 418 - 427, 1988.
[Potts i in., 1994] C. Potts, K. Tkahashi, A. I. Anton Inquiry-based requirements
analysis, „IEEE Software", t. 11, nr 2, str. 21 - 32, 1994.

[Pressman, 2009] R. S. Pressman Software Engineering: A Practitioner's


Approach, wyd. siódme, McGraw-Hill, 2009.
[Purvis i in., 1996] M. Purvis, M. Purvis, P. Jones „A group collaboration tool
for software engineering projects", Conference proceedings
of Software Engineering: Education and Practice (SEEP'96),
Dunedin, NZ, styczeń 1996.
[Rational, 2002] Rational Corp. Rational Rose. Cupertino, CA, 2002,
http://www. rational, com.
[Rational] Rationale, http://www.rational.com.
[Raymond, 1998] E. Raymond The cathedral and the bazaar, dostępne pod adresem
http://www. tuxedo, org/~esr/writings/cathedral-bazaar/
cathedral-bazaar.html, 1998.

[Ritchie i Thompson, 1974] D. M. Ritchie, K. T h o m p s o n The Unix Time-sharing System,


„Communications of the ACM", t. 17, nr 7, str. 365 - 371,
lipiec 1974.

[RMI, 2009] lava Remote Method Invocation, JDK Documentation,


Javasoft, 2009.
844 Dodatek C • Bibliografia

[Rochkind, 1975] M. J. Rochkind The Source Code Control System,


„IEEE Transactions on Software Engineering", SE-1(4),
str. 255 - 265, 1975.
[Rogers i in., 1986] The Presidential Commission on the Space Shuttle Challenger
Accident Report, Washington, DC, 6 czerwca 1986.
[Rowen, 1990] R. B. Rowen Software project management under incomplete
and ambiguous specifications, „IEEE Transactions on Engineering
Management", t. 37, n r 1, 1990.
[Royce, 1970] W. W. Royce „Managing the development of large software
systems" w: Tutorial: Software Engineering Project Management,
IEEE Computer Society, Washington, DC, str. 118 - 127,1970.
[Royce, 1998] W. W. Royce Software Project Management: A Unified
Framework, Addison-Wesley, Reading, MA, 1998.
[Rubin, 1994] J. Rubin Handbook of Usability Testing, Wiley, New York,
1994.
[Rumbaugh i in., 1991] J. Rumbaugh, M. Błaha, W. Premerlani, F. Eddy, W. Lorensen
Object-OrientedModeling and Design, Prentice Hall, Englewood
Cliffs, NJ, 1991.
[Schlumberger] Schlumberger h ttp:// www. cyberflex. com/.
[Schmidt, 1997] D. C. Schmidt „Applying design patterns and frameworks
to develop object oriented c o m m u n i c a t i o n software" w:
Peter Salus (red.) Handbook of Programming Languages, t. 1,
MacMillan Computer, 1997.
[Schwaber i Beedle, 2002] K. Schwaber, M. Beedle Agile Software Development with Scrum,
Prentice Hall, Upper Saddle River, NJ, 2002.
[Schwaber, 1995] K. Schwaber „Scrum Development Process", Business Object
Design and Implementation Workshop, OOPSLA'95, Austin,
TX, październik 1995.
[Schwaber, 2004] K. Schwaber Agile Project Management With Scrum, Microsoft,
Redwood, 2004.
[Shaw i Garlan, 1996] M. Shaw, D. Garlan Software Architecture: Perspectives on
an Emerging Discipline, Prentice Hall, Upper Saddle River,
NJ, 1996.
[Shipman i McCall, 1997] F. M. Shipman III, R. J. McCall Integrating different perspectives
on design rationale: Supporting the emergence of design
rationale from design communication, „Artificial Intelligence
in Engineering Design, Analysis, and Manufacturing", t. 11,
n r 2, 1997.
[Siewiorek i Swarz, 1992] D. P. Siewiorek, R. S. Swarz Reliable Computer Systems: Design
and Evaluation, wyd. drugie, Digital, Burlington, MA, 1992.
[Simon, 1970] H. A. Simon The Sciences of the Artificial, MIT Press, Cambridge,
MA, 1970.
[Sommerville, 2006] I. Sommerville Software Engineering, wyd. ósme
Addison-Wesley, Reading, MA, 2006.
Bibliografia 845

[Spivey, 1992] J. M. Spivey The Z Notation, A Reference Manual, wyd. drugie,


Prentice Hall International, Hertfordshire, U.K., 1992.
[Steelman, 1978] Requirements for high order computer programming languages:
Steelman, U.S. Department of Defense, Washington, DC, 1978.

[Subrahmanian i in., 1997] E. Subrahmanian, Y. Reich, S. L. Konda, A. Dutoit,


D. Cunningham, R. Patrick, M. Thomas, A. W. Westerberg
„The n-dim approach to building design support systems",
Proceedings ofASME Design Theory and Methodology
DTM'97, ASME, New York, 1997.
[Suchman, 2007] L. A. Suchman Human-Machine Reconfiguration: Plans and
Situated Actions, wyd. drugie Cambridge University Press, 2007.
[Sun, 2009] Sun Microsystems, Code Conventions for the Java Programming
Language, http://www.java.sun.com/docs/codeconv/, 2009.
[Tanenbaum, 1996] A. S. T a n e n b a u m Computer Networks, wyd. trzecie. Prentice
Hall, Upper Saddle River, NJ, 1996. Wydanie polskie Sieci
komputerowe, wyd. Helion 2004.

[Takeuchi i Nonaka, 1986] H. Takeuchi, I. Nonaka The New New Product Development
Game, „Harward Business Review", 1986.
[Taylor, 1911] F. W. Taylor The Principles of Scientific Management, Harper
Bros., New York, 1911.
[Telelogic] Telelogic, http://www. telelogic.se.

[Tichy, 1985] W. Tichy RCS — A system for version control, „Software


Practice and Experience", t. 15, nr 7,1985
[TogetherSoft, 2002] TogetherSoft, Together Control Center, Raleigh, NC,
http://www.togethersoft.com, 2002.
[Tolkien, 1995] J. R. R. Tolkien The Lord of The Rings, Harper Collins, 1995.

[Tunde i Ray, 1994] A. O. Babatunde, W. H. Ray Process Dynamics, Modeling


and Control, Oxford University Press, 1994.
C. D. Turner, D. J. Robson „The state-based testing of
[Turner i Robson, 1993]
object-oriented programs" Conference on Software
Maintenance, str. 302 - 310, wrzesień 1993.
M. Twain Following the Equator: A Journey Around the World,
[Twain, 1897]
American Publishing Co., Hartford, 1897, BoondocksNet
Edition, 2003.
D. Vaughan The Challenger Launch Decision: Risky Technology,
[Vaughan, 1996]
Culture, and Deviance at NASA, The University of Chicago
Press, Chicago, 1996.
S. Viller, I. Sommerville „Social analysis in the requirements
[Viller i Sommerville, 1999]
engineering process: f r o m ethnography to method",
International Symposium on Requirements Engineering
(ISRE'99), Limerick, Ireland, czerwiec 1999.
[Warmer i Kleppe, 2003] J. Warmer, A. Kleppe The Object Constraint Language: Getting
Your Modes Ready for MDA, wyd. drugie, Addison-Wesley,
Reading, MA, 2003.
846 Dodatek C • Bibliografia

[Weinand i in., 1988] A. Weinand, E. Gamma, R. Marty „ET++ — An object-oriented


application framework in C++" w: Object-Oriented Programming
Systems, Languages, and Applications Conference Proceedings,
San Diego, CA, wrzesień 1988.
[Weinberg, 1971] G. M. Weinberg The Psychology of Computer Programming,
Van Nostrand, New York, 1971.
[Wigand i in., 1997] R. T. Wigand, A. Picot, R. Reichwald Information,
Organization and Management: Expanding Markets and
Corporate Boundaries, John Wiley & Sons, London, 1997
[Wiki] Wiki, http://c2. com/cgi/wiki? Wiki Wiki Web.
G. Wilson, J. Ostrem WebObjects Developers Guide, Apple,
[Wilson i Ostrem, 1999] Cupertino, CA, 1998.
R. Wirfs-Brock „Design objects and their interactions:
[Wirfs-Brock, 1995] A brief look at responsibility-driven design" w: J. M. Carroll
(red.) Scenario-Based Design: Envisioning Work and
Technology in System Development, Wiley, New York, 1995.
[Wirfs-Brock i in., 1990] R. Wirfs-Brock, B. Wilkerson, L. Wiener Designing
Object-Oriented Software, Prentice Hall, Englewood Cliffs,
NJ, 1990.
[Wood i Silver, 1989] J. W o o d , D. Silver Joint Application Design®, Wiley,
New York, 1989.
[Wordsworth, 1992] J. B. Wordsworth Software Development with Z: A Practical
Approach to Formal Methods in Software Engineering,
Addison-Wesley, Reading, MA, 1992.
[Yahoo] Yahoo, http://groups.yahoo.com/.
E. Yourdon, L. L. Constantine Structured Design, Prentice
[Yourdon i Constantine, 1979]
Hall, Englewood Cliffs, NJ, 1979.
R. E. Zultner TQM for technical teams, „ C o m m u n i c a t i o n s
[Zultner, 1993]
of the ACM", t. 36, nr 10, str. 79 - 91, 1993.
Skorowidz

@see, 429 aktywności, 43, 47, 68, 100, 124, 640, 689
«boundary», 106 partycje aktywności, 103
«control», 106, 216 aktywności analizy wymagań, 217
«create», 224 aktywności etapu projektowania systemu, 305
«entity», 106 aktywności inżynierii oprogramowania, 49
« extent», 106 analiza, 51
«include», 106 implementowanie, 53
«invariant», 408 projekt systemu, 51
«postcondition», 408 projektowanie obiektów, 53
«precondition», 408 testowanie, 54
«sut», 540 zbieranie wymagań, 50
«testCase», 540 aktywności klasycznego zarządzania projektem, 653
«testComponent», 540 aktywności organizacyjne, 146
«testContext», 540 dołączanie do infrastruktury komunikacyjnej, 146
«testObjective», 540 dołączanie do zespołu, 146
udział w zebraniach zespołu, 147
aktywności projektowania systemu, 288, 303
A aktywności racjonalizacji, 567
aktywności realizacji celów projektowych, 306
Abstract Factory, 772 aktywności specyfikowania interfejsów, 416
Abstract Window Toolkit, 276 aktywności zbierania wymagań, 166
abstrakcyjne typy danych, 72 aktywności „zwinnej" realizacji projektu, 673
action items, 561, 646 aktywny przegląd projektu, 507
activities, 640, 646 ALAP, 650
activity-centered, 685 Albrecht A. J., 666
Ada, 606 alfa-testowanie, 530
adaptacyjne tworzenie oprogramowania, 737 algorytm szyfrowania, 318
Adapter, 365, 366, 371,773 alternatives, 551
delegowanie, 373 Ambler S., 739, 771
dziedziczenie, 373 analityczny model obiektowy, 64, 213, 214, 401
Adaptive Software Development, 737 analityk, 239
adaptowalność, 162 analiza, 48,49,51,212
agenda przeglądu klienckiego, 131 analiza opisu przypadków użycia, 219
agenda spotkania, 142 analiza wymagań, 159, 211, 236
agile, 637, 673 aktywności, 217, 237
Agile Alliance Manifesto, 737 analityczny model obiektowy, 214
agregacja, 92, 231 ARENA, 245
agregacja współdzielona, 231 cele, 212
kompozycyjna, 231 diagramy sekwencji, 224
agregat CM, 602, 603 dokument analizy wymagań, 238
agregat zarządzania konfiguracją, 602 dokumentowanie, 238
Airbus A320, 598 identyfikacja agregacji, 231
akceptacja systemu, 672 identyfikacja atrybutów, 232
akcje, 99 identyfikacja obiektów brzegowych, 220, 250
aktorzy, 79, 80 identyfikacja obiektów encji, 218, 245
848 Skorowidz

analiza wymagań statystyki systemu, 478


identyfikacja obiektów sterujących, 222, 251 wielokrotne wykorzystywanie rozwiązań
identyfikacja skojarzeń, 228 wzorcowych, 390
iteracje modelu analitycznego, 241 wzorce projektowe, 390
komunikacja, 240 argumentacja, 552
koncepcje, 214 arguments, 552
konsolidacja modelu analitycznego, 254 argumenty, 559
model dynamiczny, 214 argumenty komunikatu, 95
modelowanie interakcji między obiektami, 228, Ariane 501,114
252 ArrayList, 74
modelowanie relacji dziedziczenia między artifact road map, 728
obiektami, 234 As late as possible, 650
modelowanie zachowania poszczególnych As soon as possible, 650
obiektów uzależnionego od ich stanu, 233 ASAP, 649, 650
obiekty brzegowe, 215 ASD, 737
obiekty encji, 215 asSet(), 414
obiekty sterujące, 215 asynchroniczna komunikacja grupowa, 145
odwzorowywanie przypadków użycia w obiekty, asynchroniczne kolekcjonowanie racjonalizacji, 577
224 asynchroniczne mechanizmy komunikacyjne, 138,
przeglądy modelu analitycznego, 235 140
przydzielanie odpowiedzialności, 239 ATRACT, 743, 762
uzgodnienie modelu analitycznego z klientem, cel projektu, 743
243 kontrola, 745
weryfikacja modelu analitycznego, 254 modelowanie, 745
zarządzanie analizą wymagań, 237 planowanie, 744
analiza zorientowana obiektowo, 76 powtarzalność, 744
AND decomposition, 566 procesy cyklu życiowego, 745
Andres C, 485, 633, 725 projekt, 743
API, 123, 270, 357 środowisko projektu, 744
Application Programmer Interface, 270, 357 wnioski, 746
arbiter, 540 wynik, 745
Arbiter, 540 atrybut klasy, 74
architecture-centric management, 655 atrybuty, 232, 247, 469
architekt, 239,331 audyt, 601
architekt systemu, 123 audytor, 629
architektura, 266 automatyczne weryfikowanie spełnienia
architektura czterowarstwowa, 286, 287 kontraktów, 465
architektura filtr-potok, 286 automatyzacja testowania, 538
architektura klient-serwer, 283, 284 awaria, 324, 325, 346, 494, 499, 500
architektura MVC, 281 AWT, 276
architektura oprogramowania, 279, 296
architektura otwarta, 275 ^
architektura P2P, 284 ^
architektura peer-to-peer, 284 Babatunda A. O., 740
architektura repozytoryjna, 279, 280 ^ ^ w 602) ^
architektura SOA, 348 ,
i• , bag, 413
architektura trojwarstwowa, 285 Baker P 543
architektura warstwowa, 268 , „
bali, 271
architektura zamknięta, 275 ball-and-socket, 270
ARENA, 57, 190,245,303 r> t T T i ci
Baron J. T. T., 151
awSXaSań'245 baseline, 597
Basic Support for Cooperative Work, 145
deklaracja problemu, 190 Bass ^ ^ 34g
odwzorowywanie modelu na kod, 478 baza danych 311 469
projektowanie systemu, 334 tabel^ ^
specyfikowanie interfejsów, 433
Skorowidz 849

Beck K., 228, 258, 485, 633, 725 Carr M. J., 669
Beedle M., 674, 678, 725 Carroll J. M., 169, 205, 592
benchmark test, 530 CASE, 55, 452, 748
Bersoff E. H., 602 Catalysis, 49
beta-testowanie, 526, 530 cechy specyfikacji, 164
bezpieczeństwo, 36,162, 290, 568 cele analizy, 212
biblioteka, 603, 606 cele projektowe systemu, 290
biblioteka dynamiczna, 606 cele testowania, 540
biblioteka kontrolowana, 606 Centralized Traffic Control, 555
biblioteka statyczna, 606 Champty J., 166
biblioteki klas, 383 change request, 602
bieżące kolekcjonowanie racjonalizacji, 553 change set, 608
big-bang testing, 521 chaordic system, 766
Binder R. V., 542 Charette R. N., 669
Birrer E. T., 382 chief programmer organization, 679
blackboard systems, 280 Christerson M., 107
blackbox tests, 504 chroniona operacja, 405
Błaha M., 311, 348, 449, 472 chroniony atrybut, 405
blueprint, 46 chronometrażysta, 142
błąd roku 1900, 36 Chung L., 565
błąd roku przestępnego, 36 ciągi, 411,413
błędny stan, 494, 499, 500, 502 C l asS) Responsibilities i Collaborators, 228
błędy, 368,494 ClearCase, 610, 611
błędy w oprogramowaniu, 324 Clements P., 297, 348
BNF 6 0 9
> CM aggregate, 602
Boehm B., 590, 669, 677, 687, 701 C M M j 695
Booch G., 107,108,162, 215, 703 Cockburn A., 173, 206, 725, 737
Borghoff U. W., 151 Cocoa, 378
Borning A., 440 COCOMO II, 677, 727
bottom-up testing, 521, 522 C o h n M > 67?5 7 3 8
brak klienta, 718 Collection, 74
breakage, 668 Command, 775
Bridge, 368, 774 competitor testing, 530
Brooks F. P., 664, 678 Composite, 776
Brown W. J., 771 Computer Aided Software Engineering, 55
Bruegge B., 382 Concurrent Version System, 611, 633
brygady tygrysów, 529 configuration, 602
BSCW, 145 configuration item, 602
bucket theory of the mind, 41 configuration management aggregate, 602
budowanie infrastruktury, 643 Conklin J 592
budowanie wieloetapowe, 631 consequent issues, 557
budzet, 36 Constantine L. L., 107, 206, 296
bus 494
continuus integration, 630
Burgess-Yakemovic K. C„ 592 Contracts 440
buried association, 472 CORBA, 276, 277, 283
burn down chart, 676
core use cases, 703
burza mózgów, 133, 228, 241, 242, 333
° correction, 499
Buschmann F., 296, 394, 771 CRC 22g
criteria, 552
(] critical path, 649
cross reference, 429
cache'owanie wyników czasochłonnych obliczeń, 457 CruiseControl, 631
callback, 285, 383 Crystal Clear, 737
całościowe zarządzanie jakością, 740 CTC, 555, 568
Campbell R. H., 382 kontrola dostępu, 583
Capability Maturity Model, 632, 695 opis systemu, 569
850 Skorowidz

Cunningham W., 228, 258 problem, 642


Curtis B., 723 projekt, 116
CVS, 608, 609,610,611,633 projekt wysokopoziomowy, 656
cykl życiowy oprogramowania, 56, 637, 683, 686 skojarzenia, 236
aktywności, 689 wyjątki związane z naruszeniem kontraktów, 448
dojrzałość modeli, 695 zakres projektu, 643
grupy procesów, 688 założenia kontroli dostępu, 312, 340
IEEE 1074, 687, 688 definiowany model kontroli procesów, 740
iteracyjne modele ukierunkowane na aktywności, deklaracja problemu, 129,130, 642, 654, 655
701 dekompozycja dysjunkcyjna, 566
modele, 685, 698 dekompozycja hierarchiczna, 275
modele ukierunkowane na aktywności, 685 dekompozycja koniunkcyjna, 566
modele ukierunkowane na encje, 685, 706 dekompozycja systemu, 263,269, 278, 295, 332,401
modelowanie cyklu życiowego, 690 delegowanie, 359, 363
postrealizacja, 693 delegowanie implementacji, 363
prerealizacja, 691 delegowanie we wzorcach projektowych, 364
proces, 688 deliverable, 640, 648
procesy integralne, 694 delty, 608
procesy międzyrealizacyjne, 694 Deming E. W., 740
realizacja, 692 demonstrowanie prototypów, 666
sekwencyjne modele ukierunkowane dependability, 162
na aktywności, 699 design by contracts, 440
tworzenie systemu, 692 diagramy aktywności, 68, 79, 101
ujęcie produktowe, 686 decyzje, 101
zależności czasowe między aktywnościami, 686 partycje aktywności, 103
zarządzanie projektem, 690 rozwidlenia, 102
cykle, 701, 703 węzły kontrolne, 101
czas, 43 zastosowania, 103
czas przestoju, 649 złączenia, 102
czas reakcji, 162 diagramy De Marco, 72
czas trwania projektu, 719 diagramy interakcji, 64, 67, 79, 95
częstotliwość zmian, 719 argumenty, 95
członkowie projektu, 116 komunikaty, 95
czynniki ryzyka, 670, 671 zastosowanie, 97
czytelność modelu projektu systemu, 328 diagramy klas, 52, 64, 65, 78, 86, 255, 328
agregacja, 92
D dziedziczenie, 93
klasy, 87
dane, 303 klasy skojarzeniowe, 88
dane trwałe, 309 krotność skojarzenia, 89
Dart S., 601 kwalifikowanie, 92
dashboard, 631 łącza, 88
Date C. J., 469 metody, 94
De Marco T., 107,218 obiekty, 87
dead reckoning, 684 operacje, 94
deadlock, 284 redukcja krotności, 92
debugowanie, 495,496 role, 89
Decision Representation Language, 562, 564 skojarzenia, 88
decisions, 552 skojarzenia kwalifikowane, 92
decyzje, 101,120, 552 zastosowania, 94
decyzje implementacyjne, 72 diagramy komunikacyjne, 97, 282
defekty, 494 diagramy PERT, 532
definiowanie diagramy przypadków użycia, 64, 65, 78, 79
atrybuty, 236 aktorzy, 79
cele projektowe, 290 przypadki użycia, 79
relacja dziedziczenia, 84
Skorowidz 851

relacja komunikacji, 82 dołączanie do zespołu, 118, 146


relacja rozszerzania, 83 DOORS, 187
relacja zawierania, 82 doskonalenie przypadków użycia, 160, 173,198
scenariusze, 85 dostarczenie systemu, 644
zależności komunikacyjne, 82 dostępność, 162, 335, 568
diagramy sekwencji, 52, 67, 224, 225, 228, 252 doświadczenie uczestników, 717
diagramy sieciowe, 640, 649 Douglass B. P., 258
diagramy stanów, 67, 79, 98, 233 Doyle M., 151
akcje, 99 DRL, 562, 564
aktywności, 100 dry run, 150
przejście między stanami, 98 Dutoit A. H., 586, 592
przejście wewnętrzne, 100 Duval P., 630
stan, 98 dwukierunkowe skojarzenia „jeden do jednego", 459
zagnieżdżone maszyny stanów, 100 dynamiczna kontrola dostępu, 317
zagnieżdżony stan, 100 dynamiczne witryny WWW, 385
zastosowania, 101 dynamika zmian, 433
diagramy UML, 43 dyskusja rozproszonych uczestników w czasie
diagramy wdrażania, 304 rzeczywistym, 144
Dijkstra E. W., 296,440 dziedziczenie, 73, 93, 216, 359, 360
Directory, 91 dziedziczenie implementacyjne, 360, 361, 362
dojrzałość modeli cyklu życiowego, 695 dziedziczenie interfejsu, 362
dokładność, 162 dziedziczenie kontraktów, 424
dokonywanie podziału pracy, 657 dziedziczenie specyfikacyjne, 360, 362
dokument analizy wymagań, 188, 238 dziedziczenie we wzorcach projektowych, 364
system obecnie użytkowany, 189 subklasy, 93
system proponowany, 189 superklasy, 93
wprowadzenie, 188 dziedzina aplikacyjna, 76,130
dokument projektu obiektów, 402, 425,428 dziedzina realizacyjna, 76
budowanie, 426
interfejsy klas, 429 E
pakiety, 429
utrzymywanie materiału źródłowego, 431 earned value, 667
wstęp, 428 egoless programming, 679, 732
dokument projektu systemu, 328, 329 egzemplarze, 72
architektura istniejącego systemu, 329 Eiffel, 440, 465
model projektu nowego systemu, 329 ekspert komponentu, 389
usługi oferowane przez podsystemy, 330 Ekspert ma zawsze rację, 591
wprowadzenie, 329 ekspert wzorca projektowego, 389
dokument RAD, 238 ekstender klasy, 403
dokument SDD, 328 elementy działania, 561, 646
dokument umowy projektu, 642 elementy konfiguracji, 602, 603
dokumentacja powtarzalnego rozwiązania, 388 elementy ryzyka, 669
dokumentalista techniczny, 390, 432 Elssamadisy A., 771
dokumentowanie e-mail, 140
analiza wymagań, 238 emisja, 117, 134, 602, 605, 616
projektowanie obiektów, 425 Engineering Change Proposal, 602
engineering workflows, 704
projekt systemu, 328
entity-centered, 685
racjonalizacje, 585
Erl T., 348
testowanie, 532
Erman L. D., 280
transformacje, 475
erroneous state, 494, 499
wykorzystywanie gotowych rozwiązań, 388
error, 494
zarządzanie konfiguracją, 627
error guessing, 512
zbieranie wymagań, 188
etap zbierania wymagań, 50
dołączanie do infrastruktury komunikacyjnej, 118,
ewentualności, 551
146
852 Skorowidz

ewolucja elementu konfiguracji, 608 frameworki pośredniczące, 382


ExecuteTrip, 289 NFR, 565
exists(), 415 Freeman-Benson B., 440
extreme Programming, 485, 520, 526, 633, 725, 731 FRIEND, 79, 167, 743, 746, 762
aktywności cyklu życiowego projektu, 751
cel projektu, 746
F dokumenty systemowe, 749
FAA, 599 iteracje, 753
Fabryka abstrakcyjna, 376, 390, 772 kontrola, 750
delegowanie, 376 modelowanie, 748
dziedziczenie, 376 planowanie, 747
zastosowanie, 390 powtarzalność, 747
Facade, 777 procesy cyklu życiowego, 750
facilitator, 142 scenariusze, 170
Facilitator, 143 środowisko projektu, 747
Fagan M. E., 496, 541 wnioski, 753
failure, 494, 499 wynik, 752
faks, 138 zespoły funkcyjne, 750
falsyfikacja modeli, 77, 495 funkcje projektowe, 646
Fasada, 295, 777 FURPS, 162
FAT, 91 FURPS+, 162
fault, 494, 499
Fayad M. E., 382 G
faza definiowania projektu, 116
Feature-Driven Design, 725 Gaffney J. E. Jr., 666
Feiler P., 771 gałęzie, 607
Felsing J., 725 Gamma E., 283, 295, 308, 394, 771
field tests, 530 Garlan D., 296
Files, 91 generalizacja, 216, 234, 360
filtrowanie pakietów, 316 generowanie dokumentu ODD, 428
filtry, 286 gIBIS, 592
Finish no earlier than, 650 Gladwin T., 719
Finish no later than, 650 Gleick ]., 766
firewall, 315 globalna tabela dostępu, 314
Fisher R., 151,589 globalny przepływ sterowania, 319, 341
flat staffing, 664 Glover A., 630
Flower M., 108 główny architekt, 432, 477
Floyd R. W., 440 gniazda TCP/IP, 276
fly-by-wire, 598 gniazdo, 270
FNET, 650 Goldberg A., 394
FNLT, 650 gra planistyczna, 733
follproof, 496 gradual staffing, 664
forAll(), 415 graf PERT, 126
foreign key, 470 graf przepływów, 513
formowanie zespołów, 643, 663 graficzne reguły reprezentowania widoków, 71
formularz formalizujący żądanie zmiany, 138 graniczne przypadki użycia, 323
formułowanie ograniczeń, 423 Grenning ]., 677, 738
forward engineering, 427, 452, 706 grep, 287
Fowler M., 394, 450,457, 737, 771 groupware, 577, 578
Frames, 394 grupa procesów, 688
framework, 383, 384 grupa produktów, 640
frameworki aplikacyjne, 381, 382 grupowanie klas, 105
frameworki białoskrzynkowe, 382 grupowanie przypadków użycia, 104
frameworki czarnoskrzynkowe, 383 grupy dyskusyjne, 140
frameworki infrastrukturalne, 382 Guttag ]., 440
Skorowidz 853

H hierarchiczna organizacja projektu, 120


Highsmith J., 725, 735, 737
Halstead M. H., 666 Hoare C. A. R., 440
Hammer M., 166 Hock D., 766
Hamu D. S., 382 Hofmeister C, 297
Harel D., 98 hook methods, 382
harmonogram, 115, 126, 640, 646 Hopper G. M., 541
graf PERT, 126 HTTP, 283, 384
ścieżka krytyczna PERT, 127
wykres Gantta, 126
I
harmonogram przeglądów, 118, 150
HashMap, 74 IBIS, 562, 563, 592
Hayes-Roth F„ 280 iContract, 440, 468
HEARSAY II, 280 idealny tydzień, 733
Helm R., 283 identyfikacja
Henderson V. D., 602 agregacje, 231
hermetyzacja algorytmów, 780 agregaty CM, 613
hermetyzacja hierarchii, 378 aktorzy, 160, 167, 192
hermetyzacja kontekstu, 373 atrybuty, 232
hermetyzacja kosztownych obiektów, 779 cele projektowe, 290, 335
hermetyzacja niekompatybilnych komponentów, 371 elementy konfiguracji, 600, 613
hermetyzacja platformy, 376, 772 frameworki aplikacyjne, 381
hermetyzacja podsystemów, 295, 777 obiekty brzegowe, 220, 250
hermetyzacja przechowywania danych, 368 obiekty encji, 218, 220, 245
hermetyzacja przepływu sterowania, 377, 775 obiekty modelu analitycznego, 179
heurystyki Abbotta, 218 obiekty sterujące, 222, 251
heurystyki identyfikowania wzorców projektowych, obiekty trwałe, 310, 339
381 podsystemy, 294, 336
heurystyki komunikowania racjonalizacji, 588 przypadki użycia, 160, 171, 195
heurystyki metodologiczne, 765 relacje, 198
heurystyki pisania przypadków użycia, 175
relacje między aktorami a przypadkami użycia, 176
heurystyki pisania scenariuszy, 175
relacje między przypadkami użycia, 160
heurystyki pomocne w czytelnym formułowaniu
scenariusze, 160, 169, 192
ograniczeń, 423
skojarzenia, 228
heurystyki pomocne w grupowaniu obiektów
trwałe dane, 309
w podsystemy, 295
umiejętności, 660
heurystyki pomocne w identyfikowaniu atrybutów, 233
usługi, 321, 343
heurystyki pomocne w identyfikowaniu obiektów, 181
warunki graniczne, 323, 345
heurystyki pomocne w identyfikowaniu obiektów
wymagania pozafunkcyjne, 160,182, 184, 204
brzegowych, 221
identyfikatory wersji, 606
heurystyki pomocne w identyfikowaniu obiektów
identyfikowalna specyfikacja, 165
encji, 220
identyfikowalność, 160, 187
heurystyki pomocne w identyfikowaniu obiektów
idiotoodporność, 496
sterujących, 223
IEEE 1074, 687, 688, 698, 709
heurystyki pomocne w identyfikowaniu skojarzeń, 230
implementator, 123, 403
heurystyki pomocne w odwzorowywaniu naruszenia
implementowanie, 53
kontraktów w wyjątki, 468
includesO, 414
heurystyki weryfikowania kompletności zestawu infrastruktura komunikacyjna, 118, 660
przypadków użycia, 182 inicjowanie projektu, 690
heurystyki wspomagające rysowanie diagramów Inline Class Refactoring, 457
sekwencji, 227 inspection, 495
heurystyki wspomagające zarządzanie gałęziami, 621 inspekcja kodu, 132, 495, 666
heurystyki wyboru między relacjami zawierania inspekcja komponentu, 506, 507
i rozszerzania, 179 inspekcja problemu, 117
heurystyki wyboru wzorców projektowych, 379, 781 instalowanie systemu, 644, 672
854 Skorowidz

instancja klasy, 74
instancje aktorów uczestniczących, 86 J
int, 72 JAA, 599
integracja ciągła, 630, 633 Jacobson I., 107, 108, 162, 205, 215, 709
integrowanie, 553 JAD, 160, 185
integrowanie pionowe, 521, 525 agenda sesji, 185
integrowanie poziome, 521 badania, 185
interakcje między uczestnikami projektu, 120 definiowanie projektu, 185
interdyscyplinarność, 22 dokument końcowy, 185
interfejs API, 123 przygotowania, 185
interfejs podsystemów, 270 sesja, 185
interfejs programisty, 270, 357 słownik menedżera, 185
interfejs użytkownika, 378 wstępna specyfikacja, 185
interfejs wzorca, 365 wywiady, 185
interfejsy, 285, 399 JAMES, 136, 743, 754, 762
internacjonalizacja, 162 aktywności, 757
internal work products, 648 cel projektu, 754
intersection(), 414 iteracje, 756
inv:, 415 kontrola, 759
inżynier API, 123 modelowanie, 756
inżynieria, 35 planowanie, 755
inżynieria interfejsu, 165 powtarzalność, 756
inżynieria odwracająca, 427, 449, 452, 706 procesy cyklu życiowego, 757
inżynieria oprogramowania, 35, 38, 266, 716 środowisko projektu, 755
aktywności, 49 wnioski, 760
koncepcje, 43 wynik, 760
modelowanie, 38, 39 Java, 72, 258, 269
niepowodzenia, 36 klasy abstrakcyjne, 74
pozyskiwanie wiedzy, 38, 41 Java RMI, 276, 283
proces sterowany racjonalizacją, 38 JavaCard, 758
racjonalizacja, 42 Javadoc, 429, 440
rozwiązywanie problemów, 38, 40 j Contractor, 440
inżynieria pierwotna, 165 jeden do jednego, 90
inżynieria postępująca, 427, 448, 452, 706 jeden na wiele, 90
inżynieria wahadłowa, 706 jednokierunkowe skojarzenia „jeden do jednego", 459
inżynieria wtórna, 165 jednolity proces, 532, 725
inżynieria wymagań, 157 jednolity proces wytwarzania oprogramowania, 703
inżynierskie przepływy pracy, 704 jednostki pracy, 640
Islam N., 382 jednoznaczność specyfikacji, 164
issue, 551 Jensen R. W., 687
język
Issue-Based Information System, 562, 563 Ada, 606
issue-based life cycle model, 707 Eiffel, 440
iteracje modelu analitycznego, 241 Java, 72
burza mózgów, 242 język z wbudowaną kontrolą typów danych, 72
dojrzewanie, 242 OCL, 107, 359, 407
ustalanie, 242 SQL, 470
iteracje projektowania systemu, 333 UML, 43, 63, 64
iteracyjne modele ukierunkowane na aktywności, 701 Johnson R., 283
jednolity proces wytwarzania oprogramowania, Joint Application Design, 160,185
703 Jones T. C., 496
model spiralny, 701 Jonsson P., 107
iteracyjne planowanie, 738 JUnit, 538, 539
Skorowidz 855

komponenty fizyczne, 269


K komponenty logiczne, 269
kamienie milowe, 117, 118, 665 komponenty testowe, 540
karty CRC, 228, 258 Kompozyt, 378, 379, 776
karty inteligentne, 754 kompromisy projektowe, 293
katalog główny, 603, 606 komunikacja, 22, 55, 79, 113, 117
katastrofa Ariane, 114 analiza wymagań, 240
katastrofa Challengera, 638 emisje, 117
Katzenbach J. R., 677 inspekcja problemu, 117
KayA., 394 mechanizmy, 138
Kayser T. A., 151,662 projektowanie systemu, 331
Kelly J. F., 740 przeglądy klienckie, 117
Kemerer C. F., 624 przeglądy partnerskie, 117
Key Process Areas, 696 przeglądy projektowe, 117
kierownicze aspekty zarządzania konfiguracją, 627 rozwiązywanie problemów, 117
klasy, 65, 73, 86, 268 zdarzenia nieprzewidywalne, 117
atrybuty, 74, 232 zdarzenia przewidywalne, 117
dziedziczenie, 73, 93 zebrania statusowe, 117
ekstender, 403 żądanie wyjaśnień, 117
implementator, 403 żądanie zmian, 117
instancja, 74 komunikacja między uczestnikami, 432
operacje, 74 komunikacja partnerska, 122
użytkownik, 403 komunikacja planowa, 118, 128, 139
klasy abstrakcyjne, 73 burza mózgów, 133
klasy bazowe, 360 deklaracja problemu, 129
klasy bezpośrednio powiązane, 412 emisje, 134
klasy implementacyjne, 366 przeglądy klienckie, 131
klasy klienckie, 365, 403 przeglądy partnerskie, 132
klasy pochodne, 360 przeglądy post mortem, 134
klasy powiązane pośrednio, 412 przeglądy projektowe, 132
klasy rozszerzające, 367 przeglądy statusowe, 133
klasy skojarzeniowe, 88, 89, 464 komunikacja pozaplanowa, 118, 135, 136
klasy zdarzeniowe, 75 rozwiązywanie problemów, 137
klasyczne zarządzanie projektem, 653 żądanie wyjaśnień, 136
Klepp A., 407 żądanie zmian, 137
klient, 124, 239, 283 komunikatory, 140
klient pełnomocnik, 718 komunikaty, 75, 76, 94, 95
klient prezentacji, 286 argumenty, 95
klient-serwer, 283, 284 komunikowanie, 120
klimat technologiczny, 718 koncepcja wielokrotnego wykorzystania, 359
klucze główne, 470 koncepcje komunikacyjne projektu, 128
klucze kandydackie, 470 koncepcje organizacyjne projektu, 119
klucze obce, 470 koncepcje zbierania wymagań, 161
kluczowe obszary procesu, 696 konfiguracja, 602, 604
know-how, 722 konfiguracja oprogramowania, 56
Knuth D. E., 607 konfiguracja sprzętowa, 306
kolapsacja, 456 konfiguracyjne przypadki użycia, 345
kolekcje, 413 konflikty, 620, 623
kolekcje OCL, 411 konserwacja systemu, 54
kolekcjonowanie racjonalizacji, 562, 569 konsolidacja modelu, 236
komentarze Javadoc, 429, 440 konsolidacja modelu analitycznego, 254
komisja, 119 konsultanci, 124
kompletność modelu, 235 kontakt z użytkownikami, 718
kompletność modelu projektu systemu, 327 kontekst testowania, 540
kompletność specyfikacji, 164 kontrakty, 406
komponenty, 306, 384 kontrola dostępu, 304, 312, 340
856 Skorowidz

kontrola projektu, 644, 665, 675 lokalny klient samodzielny, 717


demonstrowanie prototypów, 666 lollipop, 271
inspekcje kodu, 666 Lotus Notes, 140
kamienie milowe, 665
metryki, 666
przeglądy projektu, 666 Ł
zarządzanie ryzykiem, 669 łatwość utrzymania, 162
zebrania, 665 łącza, 88
kontrola zmian, 601 łącznik, 121, 123
kontroler, 281, 629 łącznik architektoniczny, 331, 432, 477
konwersacja okazjonalna, 140
kończenie pracy systemu, 345
kończenie projektu, 671, 677 M
akceptacja systemu, 672
instalowanie systemu, 672 macierz kontroli dostępu, 314
refleksje post mortem, 673 macierz kwalifikacji, 650
koszty operacyjne, 335 Mack R. L„ 542
kółko-gniazdo, 271 MacLean A., 592
KPA, 696 magazynowanie danych, 285
Kramer R., 440, 468 Malveau R. C., 771
Kraut R. E., 151 mapa artefaktów, 728
krotki, 311 mapy stanów, 98
krotność skojarzenia, 89 Martin J., 394
Kruchten P., 709 master directory, 603
krystaliczną czystość, 737 maszyna stanu, 98
Matyas S., 630
kryteria, 552
Mayhew D. J., 543
kryteria kosztowe, 291
McCabe T., 666
kryteria pewności działania, 291
mean time between failures, 162
kryteria pielęgnowalności, 292
mechanizm odwołania zwrotnego, 285
kryteria użytkownika, 292
mechanizmy komunikacyjne, 138
kryteria wydajności, 291
asynchroniczne, 138
Kunz W., 562, 592
synchroniczne, 138,140
kursy projektowania, 26
menedżer konfiguracji, 124, 240, 331, 390, 432, 629
kursy technologiczne, 26
Menedżer ma zawsze rację, 591
kursy wprowadzające, 26
menedżer projektu, 637, 722
kwalifikowanie, 92
metamodel dziedziczenia, 362
kwantyfikatory OCL, 415
metoda Fagana, 507
kwestionariusze, 140, 141
metoda Joint Application Design, 185
metodologia Royce'a, 48, 725
L artefakty, 727
cechy, 725
Larman C., 258, 766, 771 dojrzałość procesów, 729
Leveson N. G., 348 doświadczenie dziedzinowe, 729
liczebność zespołów, 662 elastyczność procesów, 729
light weight, 737 elementy, 726
light-headed, 737 kontrola, 730
linia bazowa, 189, 597, 604 mapa artefaktów, 728
LinkedList, 74 metryki jakościowe, 730
Liskov B., 440 metryki menedżerskie, 730
listy kontroli dostępu, 314 modelowanie, 727
listy uprawnień, 314 planowanie, 726
lizak, 271, 321 powtarzalność, 727
local king client, 717 procesy cyklu życiowego, 728
Lockwood L. A. D., 206 ryzyko architektoniczne, 729
logika aplikacji, 285 skala projektu, 728
lokalne atrybuty, 412 spójność udziałowców, 729
Skorowidz 857

metodologia rugby, 111 model menedżerski, 640


iteracyjne planowanie, 738 model obiektowy, 64
kontrola, 741 model OMT, 748
modelowanie, 739 model OSI-ISO, 276, 277
planowanie, 738 model projektu obiektów, 64
powtarzalność, 739 model projektu systemu, 327, 705
procesy cyklu życiowego, 740 model przypadków użycia, 705
metodologie, 48, 713, 717, 719, 724 model racjonalizacji, 585
Boocha, 48 model sekwencyjny, 687
Catalysis, 49 model spiralny, 701
kontrola, 723 model systemu, 69
metodologia jednolitego procesu, 49 model testowania, 498, 705
modelowanie, 721 model ukierunkowany na aktywności, 685
monitorowanie, 723 model ukierunkowany na encje, 685, 706
OMT, 48 zagadnieniowy model cyklu życiowego, 707
planowanie, 719 model zadań, 640, 646, 649
powtarzalność, 720 model „zębaty", 709
procesy cyklu życiowego, 723 modelowanie, 38, 39, 69, 70, 721
przedefiniowanie celów projektu, 724 język UML, 63
Serum, 674, 737 modelowanie cyklu życiowego, 690
XP, 731 modelowanie interakcji między obiektami, 228, 252
metody, 48, 94, 717 modelowanie relacji dziedziczenia między
metody zahaczane, 382 obiektami, 234
metryki, 666 modelowanie systemów nierealistycznych, 39
Meyer B., 440 modelowanie zachowania poszczególnych obiektów
MFO, 650 uzależnionego od ich stanu, 233
miara dobroci, 559 modelowanie zorientowane obiektowo, 76
miary dojrzałości, 668 Model-View-Controller, 258, 281
miary kondycji finansowej projektu, 667 model-widok-kontroler, 258, 281
miary modularności, 668 Modula 2, 269
miary postępu technicznego, 668 modyfikacja planów projektu, 644
miary stabilności, 668 modyfikowalność, 290
middleware, 276, 355, 359, 384, 571, 744 Moran T. P., 592
mierniki ilościowe, 666 Most, 368, 774
minimalizacja ryzyka, 672 delegowanie, 370
Minsky Marvin, 394 dziedziczenie, 370
misja STS-51L, 638 Mowbray T. J., 771
mistrz, 674, 675 MSO, 649, 650
model, 43,46, 69, 70, 281, 721 MTBF, 162, 668
archiwizowanie, 722 MUE, 607
komunikowanie, 721 Must finish on, 650
projektowanie, 721 Must start on, 650
model analityczny, 94, 213, 214, 455, 705 MVC, 258, 281
model analityczny systemu planowania podróży, 288 My UML Editor, 607
model bazarowy, 619 Myers G. J., 512, 542
model cyklu życiowego oprogramowania, 685, 689, MyTrip, 307, 308, 313, 323, 325
698
model dojrzałości organizacyjnej, 695
model dynamiczny, 64, 213, 214 N
model dziedziny aplikacyjnej, 77
nadklasy, 73
model funkcjonalny, 64, 213
nadużycie interfejsu, 36
model FURPS, 162
namiastka testowa, 499, 505
model FURPS+, 162
narzędzia, 717
model implementacji, 705
narzędzia komunikacji grupowej, 144
model kaskadowy, 42, 699, 708
narzędzia wspomagające zarządzanie konfiguracją, 610
model maszyny stanów UML, 98
858 Skorowidz

nauka, 38 obiekty encji, 215, 218, 342


nauki o sztuczności, 39 obiekty modelu analitycznego, 179
nauki przyrodnicze, 38 obiekty projektu, 125
nauki społeczne, 38 obiekty proxy, 308
nawigacja, 88 obiekty realizacyjne, 359
nawigacja polinezyjska, 684 obiekty sterujące, 215, 342
nazwy obiekty trwałe, 125,310
przypadki użycia, 80 obiekty uczestniczące, 181
scenariusze, 85 Object Constraint Language, 107, 359, 407
negocjowanie specyfikacji z klientem, 185 Object Design Document, 402, 425
Netmeeting, 144 Object Modeling Technique, 48, 64
network diagram, 649 Object-Oriented Software Engineering, 64
Neumann P., 59 obsada stopniowana, 664
NFR, 562, 565, 592 Observer, 778
cele realizacyjne, 566 Obserwator, 283, 390, 778
dekompozycja, 565 zastosowanie, 393
niby-klient, 718 obsługa sytuacji wyjątkowych, 324
Niech czas zdecyduje, 591 OCL, 107, 359, 407
niejednoznaczności, 212 existsQ, 415
Nielden J., 542 forAll(), 415
niepowodzenia w inżynierii oprogramowania, 36 kolekcje, 411, 413
niezawodność, 162, 290, 494 kontrakty, 421
niezawodność oprogramowania, 494, 495 kwantyfikatory, 415
niezawodny system, 325 niezmienniki, 421
niezmienniki, 406 odwołania do atrybutów, 414
NIH, 387 ograniczenia, 408
no client, 718 operacje kolekcji, 414
Nonaka I., 677, 737 warunki końcowe, 409
Norman D. A., 206, 740 warunki wstępne, 408
Not Invented Here, 387 ODD, 402, 425, 534, 748
notacja, 48, 71 oddzielenie encji od widoków, 778
notacja Backusa-Naura, 609 Odell J. J., 394
notacja Boocha, 64 odporność na awarie, 290
notacja kółko-gniazdo, 270, 271 odwołania skrośne, 429
notacja kropkowa, 408 odwołania zwrotne, 285
notacja OMT, 64 odwzorowanie kontraktów w wyjątki, 465, 482
notacja OOSE, 64 odwzorowanie modeli klas w schematy
notacja UML, 63 przechowywania danych, 448
notatki, 105 odwzorowanie modelu na kod, 445
nowe technologie, 367 aktywności, 454
nowe widoki, 368 ARENA, 478
nowi dostawcy, 367 cele, 447
numery wersji, 606 dokumentowanie transformacji, 475
inżynieria odwracająca, 449, 452
inżynieria postępująca, 448, 452
O koncepcje, 448
obiekt uczestniczący, 67 odwzorowywanie kontraktów w wyjątki, 465, 482
obiektowa baza danych, 311 odwzorowywanie modelu obiektowego w schemat
obiektowy model projektu systemu, 64 bazy danych, 469, 484
obiekty, 73, 74, 86 odwzorowywanie skojarzeń na kolekcje, 458, 480
operacje, 94 optymalizacja modelu, 447, 455
przejście między stanami, 98 przydzielanie odpowiedzialności, 477
stan, 98 refaktoryzacja kodu, 448, 450
obiekty aplikacyjne, 359 transformacja modelu, 448, 449,453
obiekty brzegowe, 215, 220, 342 zarządzanie transformacjami, 475
zasady transformacji, 453
Skorowidz 859

odwzorowanie modelu obiektowego w schematy wyrównywanie braku kwalifikacji, 662


bazy danych, 469, 484 zebranie otwierające, 664
odwzorowanie klas i atrybutów, 470 Orr O., 736
odwzorowanie pionowe, 473 OSI-ISO, 276
odwzorowanie poziome, 475 otoczka dla starszego kodu, 773
odwzorowanie relacji dziedziczenia, 473 Overgaard G., 107
odwzorowanie skojarzeń, 472 OWL, 130, 669
skryte powiązania, 472
tabela pośrednicząca, 472
tabela skojarzeniowa, 472 P
odwzorowanie oprogramowania w konkretny sprzęt, P2P, 284, 285
303 package, 405
odwzorowanie pionowe, 473 Paech B., 586, 592
odwzorowanie podsystemów w procesory pakiet, 126
i komponenty, 306, 337 pakietowa operacja, 405
odwzorowanie poziome, 473, 475 pakietowy atrybut, 405
odwzorowanie przypadków użycia w obiekty, 224 pakiety, 104
odwzorowanie skojarzeń na kolekcje, 458, 480 grupowanie klas, 105
ogół-szczegóły, 217 grupowanie przypadków użycia, 104
ograniczenia, 106, 107, 163 pakiety pracy, 640, 646
ograniczenia ASAP, 649 Palmer S., 725
ograniczenia czasowe, 649, 650 Parnas D., 296, 507
ograniczenia MFO, 649 participants, 640
ograniczoność zasobów, 22 Partsch H., 485
okno projektowe, 334 partycje, 275
okresowe zebrania statusowe, 665 partycje aktywności, 103
OMT, 48, 64, 748 partycjonowanie, 268, 278
analiza, 48 partycjonowanie aktywności, 103
projekt obiektów, 48 Paulish D. J., 677
projektowanie systemu, 48 PaulkM. C., 695, 723
OMTool, 748 peer-to-peer, 284
OOPSLA, 258 PEM, 130
OOSE, 64,166 percepcja dwustabilna, 212
operacje, 74, 94 Perforce, 610, 611
opóźnianie kosztownych obliczeń, 457 Personal Environment Module, 130
oprogramowanie, 35 PERT, 126, 532
oprogramowanie pośrednie, 355, 359, 384 Petroski H., 739, 766
optymalizacja modelu, 357 pewność działania, 162
optymalizacja modelu obiektowego, 353, 455 piłka, 271
optymalizacja ścieżek dostępu, 455 Plan testów, 125, 497, 533, 534
OR decomposition, 566 Plan Zarządzania Konfiguracją Oprogramowania,
organizacja diagramów, 104 627, 628
organizacja głównego programisty, 679 aktywności, 628
organizacja łącznikowa, 122 harmonogram, 628
organizacja projektu, 113, 119 konserwacja planu, 628
organizacja przeglądów, 149 wstęp, 628
organizacja zespołowa, 119 zarządzanie, 628
organization chart, 640 zasoby, 628
organizowanie projektu, 659, 675 Plan Zarządzania Projektem, 651, 654, 690
formowanie zespołów, 663 Planning Poker, 677, 738
identyfikacja umiejętności, 660 planowanie, 719
podpisanie umowy projektu, 664 planowanie projektu, 654, 674
przydzielanie ról technicznych, 661 deklaracja problemu, 654, 655
przypisywanie ról menedżerskich, 661 Plan Zarządzania Projektem, 654
ustanowienie infrastruktury komunikacyjnej, 660 podział pracy, 657
wybór liczebności zespołów, 662 projekt wysokopoziomowy, 656
860 Skorowidz

planowanie projektu problemy z użytecznością, 158


SPMP, 654 proces, 688
tworzenie początkowego harmonogramu, 659 proces falsyfikacji, 78
wysokopoziomowy projekt systemu, 654 proces jednolity, 703
planowanie testów, 497, 532 proces przetwarzania wymagań, 689
PlanTrip, 288 proces rewizyjny, 244
platforma sprzętowa, 306 proces sterowany racjonalizacją, 38
pliki, 311 procesor, 306
pluskwy, 494 procesy cyklu życiowego, 723
płaska obsada, 664 procesy integralne, 694
początkowa identyfikacja obiektów modelu procesy międzyrealizacyjne, 694
analitycznego, 179 product backlog, 674
początkowy harmonogram, 659 product owner, 675
podejście rugby, 737 produkt, 43, 46,115, 124, 640, 646
podejście zorientowane obiektowo, 40 produkt docelowy, 46,115
podklasy, 73 produkt finalny, 640, 648
podmiana implementacji, 774 produkt wewnętrzny, 46, 125, 648
podpisanie umowy projektu, 664 profile, 539
podsystemy, 70, 267, 268, 294 programista, 477, 629
podtypowanie, 363 programowanie bezosobowe, 679, 732
pojedynczy projekt, 623 programowanie ekstremalne, 485, 520, 633, 725, 731
poker planistyczny, 738 aktywności, 734
Polecenie, 377, 390, 775 Częste integrowanie, 735
delegowanie, 378 emisje systemu, 733
dziedziczenie, 378 gra planistyczna, 733
zastosowanie, 392 idealny tydzień, 733
polimorfizm, 517 kontrola, 735
pomocnicze przepływy pracy, 704 modelowanie, 734
Popper K., 41, 59, 495 Najpierw testy, 734
poprawki, 499, 505 partner, 732
poprawność modelu projektu systemu, 327 planowanie, 732
poprawność specyfikacji, 164 powtarzalność, 734
PortnyS. E., 677 procesy cyklu życiowego, 734
post mortem, 644, 673 Programowanie parami, 735
post:, 409 prowadzący, 732
postrealizacja, 693 Refaktoryzacja przed rozszerzeniem, 735
potencjalnie dostarczalne przyrosty produktu, 674 system samoorganizujący się, 735
potentially deliverable product increment, 674 szybkość projektu, 733
Poter A. A., 496 testy, 734
potoki, 286 Testy dla każdej nowej usterki, 735
powtarzalna architektura, 720 zasady, 732
powtarzane trawersowanie skojarzeń, 455 programowanie sterowane problemami, 42
poziomy dojrzałości organizacji cyklu życiowego, 695 programowanie sterowane ryzykiem, 42
pozyskiwanie wiedzy, 38, 41, 42 programowanie wielowątkowości, 321
praca, 640 Project Agreement, 642, 664
praca grupowa, 577 project functions, 646
prawa dostępu, 313 project kick off meeting, 674
pre:, 408 project velocity, 733
Premerlani W., 311, 348,449, 472 projekt, 43, 56, 113,115
prerealizacja, 691 członkowie, 116
primary key, 470 faza definiowania projektu, 116
priorytety funkcja systemu, 243 faza startowa, 117
priorytetyzacja czynników ryzyka, 671 faza terminalna, 117
private, 405 faza ustalona, 117
Problem Statement, 642, 655 harmonogram, 115,126
problemy z oprogramowaniem, 37 interakcje między uczestnikami projektu, 120
Skorowidz 861

komunikacja, 117 wybór wzorców projektowych, 367


komunikacja planowa, 128 wzorce projektowe, 356, 364
komunikacja pozaplanowa, 135 zarządzanie projektowaniem obiektów, 425
koncepcje komunikacyjne, 128 zarządzanie wykorzystywaniem gotowych
koncepcje organizacyjne, 119 rozwiązań, 386
organizacja hierarchiczna, 120 zasada zastępowania Liskov, 364
organizacja projektów, 119 projektowanie sterowane cechami, 725
organizacja zespołowa, 119 projektowanie sterowane odpowiedzialnością, 166
produkt, 115, 124 projektowanie systemu, 48, 49, 51, 263, 266, 305, 552
realizacja, 116 aktywności, 288
role, 122 architektura programowa, 267
struktura łącznikowa, 121, 122 architektura warstwowa, 268
uczestnicy, 116 ARENA, 334
zadanie, 116, 124 cele projektowe, 267
zespoły, 119 definiowanie celów projektowych, 290
projekt mieszkania, 264 dokumentowanie, 328
projekt obiektów, 48, 49 doskonalenie dekompozycji stosownie do celów
projekt systemu, 51 projektowych, 263
projekt systemu informatycznego, 37 graniczne przypadki użycia, 267
projekt wysokopoziomowy, 656 hermetyzacja podsystemów, 295
projekt XP, 743 identyfikacja celów projektowych, 290
projektant obiektów, 123, 432 identyfikacja podsystemów, 294
projektowanie, 49, 721 interfejsy podsystemów, 270
projektowanie globalnego przepływu sterowania, 319, interfejsy programisty, 270
341 iteracje, 333
projektowanie niezawodnych systemów, 325 klasy, 268
projektowanie obiektów, 53, 353, 355 kompromisy projektowe, 293
aktywności, 356 komunikacja, 331
ARENA, 390 koncepcje, 267
biblioteki klas, 383 kryteria kosztowe, 291
delegowanie, 363 kryteria pewności działania, 291
dokumentowanie wykorzystania gotowych kryteria pielęgnowalności, 292
rozwiązań, 388 kryteria użytkownika, 292
dziedziczenie implementacyjne, 360 kryteria wydajności, 291
dziedziczenie specyfikacyjne, 360 odwzorowanie podsystemów w procesory
elementy, 353 i komponenty, 337
gotowe komponenty, 356, 367 partycje, 275
hermetyzacja hierarchii, 378 partycjonowanie, 268
hermetyzacja kontekstu, 373 podsystemy, 267, 268
hermetyzacja niekompatybilnych projektowanie wstępnej dekompozycji, 263
komponentów, 371 rozpoznawanie celów projektowych, 263
hermetyzacja platformy, 376 spoistość, 267, 271,272
hermetyzacja przechowywania danych, 368 sprzężenie, 267, 271
hermetyzacja przepływu sterowania, 377 style architektoniczne, 279
heurystyki wyboru wzorców projektowych, 379 usługi, 267, 270
identyfikacja frameworków aplikacyjnych, 381 warstwy, 275
komponenty, 384 zarządzanie projektowaniem systemu, 328
obiekty aplikacyjne, 359 projektowanie za pomocą kontraktów, 440
obiekty realizacyjne, 359 promocja, 602, 605, 615
optymalizacja modelu, 357 promotion, 602
przydzielanie odpowiedzialności, 389 propozycja, 557
przystosowywanie frameworków protected, 405
aplikacyjnych, 381 protokolant, 142, 587
restrukturyzacja modelu, 357 protokół z zebrania, 143, 144
specyfikowanie interfejsów, 357, 399 prototyp interfejsu użytkownika, 509
wielokrotne wykorzystanie, 359 prototyp pionowy, 334, 509
862 Skorowidz

prototyp poziomy, 334, 509 Pull Up Field, 450


prototyp z krainy Oz, 509 Pull Up Method, 450
prototypowanie, 77, 78 Purvis M., 591
prototypy, 666
proxy, 308
Proxy, 308, 457, 779 Q
proxy client, 718
QOC, 562, 564, 590, 592
prywatna operacja, 405
Questions, Options, and Criteria, 562, 564
prywatny atrybut, 405
QuestMap, 592
przebiegi, 674
przechowywanie danych, 309, 311, 339
przeczuwanie błędów, 512 R
przedefiniowanie celów projektu, 724
przeglądy, 495 racjonalizacja, 42, 55, 549, 551, 579
przeglądy klienckie, 117, 131,149 aktywności, 567
przeglądy modelu analitycznego, 235 argumentacja, 552
przeglądy partnerskie, 117,132 argumenty, 559, 570
przeglądy post mortem, 134 aspekty kierownicze, 585
przeglądy projektu, 117, 132, 666 bieżące kolekcjonowanie racjonalizacji, 553
przeglądy przebiegów, 677 CTC, 568
przeglądy statusowe, 133 Decision Representation Language, 564
przejście między stanami, 98 decyzje, 552
przejście wewnętrzne, 100 definiowanie problemów, 556
przenośność, 162 dokumentowanie, 585
przepływ sterowania, 304, 319, 341 DRL, 564
przepływ zdarzeń, 80, 86 eksploracja przestrzeni rozwiązań, 557
przepływy pracy, 704 elementy działania, 561
przepustowość, 162 ewentualności, 551
przestrzeń robocza, 606 heurystyki komunikowania, 588
przewidywalne zdarzenia komunikacyjne, 128 IBIS, 563
przydział programistów do projektu, 664 implementacja rozstrzygnięć, 561
przydzielanie obiektów i podsystemów do węzłów, Issue-Based Information System, 563
307 kolapsacja przestrzeni rozwiązań, 560
przydzielanie odpowiedzialności, 239, 330, 389, 431 kolekcjonowanie asynchroniczne, 577
przydzielanie ról technicznych, 661 kolekcjonowanie racjonalizacji, 553, 562, 569
przypadki testowe, 499, 503, 540 koncepcje, 554
przypadki użycia, 51, 79, 81, 171 kryteria, 552, 559, 570
aktorzy, 80 modele racjonalizacji, 585
doskonalenie, 173 modelowanie zagadnień, 589
identyfikacja, 171 negocjowanie zagadnień, 589
nazwa, 80 NFR, 565
przepływ zdarzeń, 80 propozycja, 557, 570
przypadki użycia dla sytuacji wyjątkowych, 346 protokolant, 587
warunki końcowe, 80 przypisywanie odpowiedzialności, 587
warunki wstępne, 80 QOC, 564
wymagania jakościowe, 80 Questions, Options, and Criteria, 564
zależności komunikacyjne, 82 redaktor racjonalizacji, 586, 587
przypisywanie ról menedżerskich, 661 rekonstruowanie racjonalizacji, 553, 582
przyrosty, 608 rozstrzygnięcie, 560
przystosowywanie frameworków aplikacyjnych, 381 strategie rozwiązywania konfliktów, 590
pseudo-client, 718 wartościowanie elementów przestrzeni
pseudowymagania, 163 rozwiązań, 559
public, 405 weryfikator racjonalizacji, 587
publiczna klasa, 405 zagadnienie, 551, 556
publiczny atrybut, 405 zintegrowanie racjonalizacji z modelami
Pull Up Constructor Body, 450 systemu, 553
Skorowidz 863

racjonalizowanie zmian, 506 reverse engineering, 427, 452, 706


RAD, 188, 206, 238, 534, 748 review, 495
Raport zdarzeń testowych, 533, 535 Revision Control System, 610, 633
raportowanie, 120 Ritchie D. M., 287
raportowanie statusu, 601 Rittel H., 562, 592
Rational Unified Process, 709 RobsonD.}., 516
rationale, 549 Rochkind M. J., 632
Ray W. H., 740 role, 44, 45, 89, 640, 646
RCS, 610, 633 role menedżerskie, 661
rdzenne przypadki użycia, 703 role międzyfunkcyjne, 123
realistyczna specyfikacja, 165 role programistyczne, 123
reałistyczność modelu, 236, 327 role techniczne, 661
realizacja celów projektowych, 301, 306 role w realizacji projektu, 122
aktywności, 306 role zarządcze, 122
realizacja projektu, 116 round-trip engineering, 706
realizacja skojarzeń, 448 Rowen R. B., 709
redaktor dokumentacji, 123, 239, 331 Royce W., 532, 677, 687, 699, 725
redaktor racjonalizacji, 586, 587 rozbudowywanie infrastruktury komunikacyjnej, 118
redukcja krotności, 92 rozmowy telefoniczne, 140
redukcja obiektów do atrybutów, 456 rozpoczynanie pracy systemu, 345
redukcja złożoności modelu, 82, 84 rozpoznawanie kwalifikacji, 643
redundancja, 302 rozproszenie geograficzne, 718
refaktoryzacja kodu, 448, 450, 485 rozstrzygnięcie, 560
wchłonięcie klasy, 457 rozszerzalność, 363
wciągnięcie metod, 450, 451 rozszerzenia diagramów, 106
wciągnięcie pola, 450 rozwiązywanie problemów, 21, 38, 40, 117, 137, 138
wciągnięcie treści konstruktora, 450, 451 rozwidlenia, 102
refleksje post mortem, 673 różnice między zawieraniem a rozszerzaniem, 178
Rehabilitation Act, 163 RPC, 283
reingeenering, 166 Rubin E., 212
reinżynieria, 166 Rubin J., 509, 542
rekonstruowanie racjonalizacji, 553, 582 rugby, 737
rekurencyjna reprezentacja hierarchii, 776 Rumbaugh J., 107,108,162, 215,455,485
relacja dziedziczenia, 84 rundy, 701
relacja komunikacji, 82 ryzyko, 669
relacja między aktorami a przypadkami użycia, 176
relacja ogół-szczegół, 217
relacja rozszerzania, 83,180 S
relacja rozszerzania między przypadkami użycia, 176 samoloty pasażerskie, 598
relacja zawierania, 82,180 sandwich testing, 521, 522
relacja zawierania między przypadkami użycia, 177 SatWatch, 161,163,187
relacje, 469 sawtooth model, 709
relacyjne bazy danych, 311, 469 scalanie gałęzi, 620
release, 602 SCCS, 632
release candidate, 525 scenariusze, 85, 130, 171
reliability, 494 instancje aktorów uczestniczących, 86
Remote Procedure Call, 283 nazwy, 85
ReportEmergency, 81,219 przepływ zdarzeń, 86
repository, 603 scenariusz bieżący, 169
repozytorium, 279, 603, 606 scenariusz ewaluacyjny, 170
REQuest, 586, 592 scenariusz treningowy, 170
Requirements Analysis Document, 188, 238 scenariusz wizjonerski, 169
RequisitePro, 187 scenopis rysunkowy, 509
resolution, 560 schedule, 646
responsibility-driven design, 166 Scheduler, 540
restrukturyzacja modelu, 353, 357
864 Skorowidz

schemat bazy danych, 311 skojarzenia kwalifikowane, 92, 463


schemat identyfikowania wersji, 606 skojarzenia niekwalifikowane, 92
schemat organizacyjny, 640 skojarzenia wiele na wiele, 461
schematy, 469 skojarzenia wielokrotne, 455
Schlichter J., 151 skojarzenie, 88, 229, 458
Schwaber K., 674, 677, 725 atrybuty, 229
sciences of the artificial, 39 skryte powiązania, 472
SCMP, 627 slack time, 649
Scrum, 674, 677, 725, 737, 739 Smalltalk-80, 258
dni robocze, 675 smoke test, 632
kontrola projektu, 675 SNET, 650
kończenie projektu, 677 SNLT, 650
mistrz, 674, 675 SOA, 348
organizowanie projektu, 675 socket, 270
planowanie projektu, 674 Software Configuration Management Plan, 627
potencjalnie dostarczalne przyrosty produktu, 674 software library, 603
przebiegi, 674 software life cycle models, 685
przeglądy przebiegów, 677 software life cycles, 637
role, 675 Software Project Management Plan, 642
schemat ogólny, 678 software reliability, 494
właściciel produktu, 675 solidność, 162
wykresy wygaszania, 675 sort, 287
zebranie planistyczne, 674 Source Code Control System, 632
zebranie startowe, 674 specjalista od zastosowań, 124
zebranie statusowe, 675 specjalista z zakresu dziedziny aplikacyjnej, 124
zespół, 675 specjalista z zakresu dziedziny realizacyjnej, 124
Serum master, 675 specjalizacja, 216, 217
Scrum of Scrums, 766 Specyfikacja przypadków testowych, 533, 534, 535
Scrum team, 675 specyfikacja usług, 353
SDD, 328, 534, 748 specyfikacja wymagań, 159, 164
sekcja 508 ustawy Rehabilitation Act, 163 identyfikowalność, 165
sekwencje abstrakcyjne, 96 jednoznaczność, 164
sekwencyjne modele ukierunkowane kompletność, 164
na aktywności, 699 poprawność, 164
model kaskadowy, 699 realizm, 165
V-Model, 700 spójność, 164
selectQ, 414 weryfikowalność, 165
self, 408 specyfikowanie interfejsów, 357, 399, 401, 403
sequence, 413 aktywności, 402, 416
Service Oriented Architecture, 348 ARENA, 433
serwer, 283 brakujące atrybuty i operacje, 402
serwer prezentacji, 286 definiowanie sygnatur, 402
sesja JAD, 185 definiowanie widzialności, 402
sesje treningowe, 118 dziedziczenie kontraktów, 424
set, 413 ekstender klasy, 403
sformułowanie problemu, 129 identyfikacja brakujących atrybutów, 417
shadow testing, 531 identyfikacja brakujących operacji, 417, 434
Shaw M., 296 implementator klasy, 403
Siegel S. G., 602 język OCL, 407
Siewiorek D. P., 348, 543 kolekcje OCL, 411
Simon H., 39 kontrakty, 406
size, 414 kwantyfikatory OCL, 415
skalowalność, 335 specyfikowanie kontraktów, 402,435, 438
skill matrix, 650 specyfikowanie niezmienników, 421
skojarzenia jeden do jednego, 459 specyfikowanie sygnatur, 418
skojarzenia jeden na wiele, 461 specyfikowanie typów, 418
Skorowidz 865

specyfikowanie warunków końcowych, 419 Straus D., 151


specyfikowanie warunków wstępnych, 419 Streeter L. A., 151
specyfikowanie widzialności, 418 struktura łącznikowa, 121,122
sygnatury, 403, 404 struktura podziału pracy, 640,646, 648,657
typy, 403 struktura raportowania organizacji, 120
użytkownik klasy, 403 strukturalizowane wywiady, 140
widzialność, 403, 405 STS-51L, 638
zarządzanie projektowaniem obiektów, 425 style architektoniczne, 279
specyfikowanie niezmienników, 421 subklasy, 73, 93, 360
SPMP, 642, 652, 654, 655, 690 subtelna kontrola, 741
spoistość, 267, 271, 272 subtle control, 741
spotkania osobiste, 140,141 Suchman L. A., 719, 720
spójność modelu, 235, 327 suchy przebieg, 150
spójność specyfikacji, 164 superklasy, 73, 93, 360
sprint backlog, 674 supporting workflows, 704
sprzęt, 43 Sutherland J., 677
sprzężenie, 267, 271 Swarz R. S., 348, 543
SQL, 471 swimlanes, 103
staged build, 631 Swing, 258, 276, 378
stan obiektu, 98 Switzer J., 151
standard cykli życiowych, 688 SYBIL, 564
stany realizacji projektu, 116 sygnały dymne, 138
Start no earlier than, 650 sygnatury, 404
Start no later than, 650 synchroniczna komunikacja grupowa, 140
statecharts, 98 synchroniczny mechanizm komunikacyjny, 138, 140
statyczna polityka dostępu, 316 syndrom „to nie nasz wynalazek", 387
stereotypy, 106, 216 syndrom Y2K, 551
«boundary», 106 system, 43, 46, 69
«control», 106, 216 system ARENA, 57, 190, 245
«entity», 106 system centralnego sterowania ruchem, 555
«extent», 106 system chaordyczny, 766
«include», 106 system CVS, 608
«invariant», 408 System Design Document, 328
«postcondition», 408 system informatyczny, 35, 37
«precondition», 408 system komputerowy promu kosmicznego, 302
«sut», 540 system plików, 91
«testCase», 540 system samoorganizujący się, 735
«testComponent», 540 system sztuczny, 39
«testContext», 540 system tablicowy, 280
«testObjective», 540 system under test, 540
sterowanie proceduralne, 319 system wysokiej jakości, 382
sterowanie wielowątkowe, 320 sytuacje wyjątkowe, 346
sterowanie zdarzeniowe, 319 szablon dokumentu RAD, 206
sterownik testowy, 499, 505 szkolenia, 662
storyboard, 509 szybkość projektu, 733
Strategia, 373, 518, 780 szyfrowanie, 318
delegowanie, 375 szyfrowanie informacji, 313
dziedziczenie, 375
strategia przechowywania danych, 311
strategia przechowywania obiektów trwałych, 340 Ś
strategia zrównoleglonych projektów, 624
strategie integrowania pionowego, 525 ścieżka krytyczna, 649
strategie integrowania poziomego, 521 ścieżka krytyczna PERT, 127
strategie minimalizowania ryzyka, 672 ścisłe dziedziczenie, 364
strategie rozwiązywania konfliktów, 590 śledzenie problemów, 506
Strategy, 780 średni czas pracy między awariami, 162, 668
r

866 Skorowidz

środowisko projektu, 716, 717, 744 TestCase, 503, 538


czas trwania projektu, 719 testowanie akceptacyjne, 497, 526, 644
częstotliwość zmian, 719 testowanie bazujące na modelach, 539
doświadczenie uczestników, 717 testowanie funkcjonalności, 497, 526
klimat technologiczny, 718 testowanie graniczne, 511
kontakt z użytkownikami, 718 testowanie hurtowe, 521
rozproszenie geograficzne, 718 testowanie instalacji, 497, 526
typ klienta, 717 testowanie ręczne, 538
środowisko testowe, 539 testowanie kanapkowe, 521, 522
testowanie modułów, 510
testowanie pilotażowe, 526, 530
T testowanie polimorfizmu, 517
T.H.E., 296 testowanie regresyjne, 506, 537
tabela pośrednicząca, 472 testowanie rywala, 530
tabela skojarzeniowa, 472 testowanie sterowane stanami, 516
tabele, 469 testowanie strukturalne, 497
Takeuchi H., 677, 737 testowanie systemu, 497, 506, 526
taksonomiczna identyfikacja ryzyka, 669 testowanie ścieżek, 512
task, 640, 646 testowanie użyteczności, 497, 506, 508
task model, 649 testowanie w cieniu, 530
Taxonomy-Based Risk Identification, 669 testowanie wahadłowców, 492
Taylor F., 719, 740 testowanie wstępujące, 521, 522
TCS, 535 testowanie wydajności, 497, 526, 528
teoria umysłu pojemnika, 41 testowanie wymaganiowe, 526
Test Case Specification, 535 testowanie zstępujące, 521, 522, 523
test driver, 499 testowany komponent, 498
test harness, 539 testy białoskrzynkowe, 504
testy czarnoskrzynkowe, 504
Test Incident Report, 535
TIR, 535
Test Plan, 497
U2TP, 539
test stub, 499
uprząż testowa, 539
TestCase, 503, 538
usterki, 499, 500
tester, 123, 124
zarządzanie testowaniem, 531
testing, 494
testowanie integracyjne, 497, 506, 519, 520
testowanie, 54, 491, 494, 496, 553
strategie integrowania pionowego, 525
aktywności, 497, 506
strategie integrowania poziomego, 521
automatyzacja, 538
testowanie jednostkowe, 497, 506, 510
awaria, 499, 500
graf przepływów, 513
błędny stan, 499, 500
pokrycie, 510
dokumentowanie, 532
reprezentatywność, 511
inspekcja komponentu, 506, 507
rozłączność, 511
lUnit, 538
testowanie graniczne, 511
koncepcje, 498
testowanie polimorfizmu, 517
namiastka testowa, 499, 505
testowanie ścieżek, 512
Plan testów, 533, 534, 536
testowe klasy równoważności, 510
planowanie testów, 497, 532
testowany komponent, 498
poprawki, 499, 505
testowany system, 540
profile, 539
testowe klasy równoważności, 510
Przydzielanie odpowiedzialności, 536
testy akceptacyjne, 530
przypadki testowe, 499, 503
testy bezpieczeństwa, 529
Raport podsumowujący, 533
testy białoskrzynkowe, 504
Raport sumaryczny, 535
testy czarnoskrzynkowe, 504
Raport zdarzeń testowych, 533, 535
testy dubletowe, 520
Specyfikacja przypadków testowych, 533, 534, 535
testy dwójkowe, 520
sterownik testowy, 499, 505
testy instalacyjne, 531
środowisko testowe, 539
testy kwadrupletowe, 520
TCS, 535
Skorowidz 867

testy na dym, 632 UML, 21, 43, 63, 64, 65, 78, 607
testy objętościowe, 529 diagramy aktywności, 68, 79, 101
testy odtwarzania, 530 diagramy interakcji, 64, 67, 79, 95
testy pilotażowe, 530 diagramy klas, 64, 65, 78, 86
testy polowe, 530 diagramy przypadków użycia, 64, 65, 78, 79
testy produktu, 509 diagramy sekwencji, 67
testy prototypu, 509 diagramy stanów, 67, 79, 98
testy przeciążeniowe, 529 diagramy wdrażania, 304
testy regresyjne, 537 dziedziczenie, 93
testy scenariusza, 508 klasy, 75
testy tripletowe, 520 komponenty fizyczne, 269
testy uwarunkowań czasowych, 529 komponenty logiczne, 269
testy wzorcowe, 530 krotność, 90
TEX, 607 maszyna stanu, 98
tępe zliczanie, 684 model dynamiczny, 64
ThingLab, 440 model funkcjonalny, 64
Thompson K., 287 model obiektowy, 64
Tichy W., 633, 771 modelowanie, 63
TicketDistributor, 44 notacja, 71
aktywności, 47 notatki, 105
dekompozycja systemu, 53 obiekty, 74
model dynamiczny systemu, 51, 52 ograniczenia, 106
model obiektowy systemu, 52 organizacja diagramów, 104
produkty, 46 pakiety, 104
przypadki użycia, 51 rozszerzenia diagramów, 106
PurchaseOneWayTicket, 51 skojarzenia, 88
role, 45 stereotypy, 106
zadania, 47 tory pływackie, 103
zasoby, 47 U2TP, 539
TIR, 535 widzialność elementu, 406
tolerowanie usterek, 495 UML 2 Testing Profile, 539
Tonies C. C., 687 umowa projektu, 642, 664
top-down testing, 521, 522 Unified Modeling Language, 48
tory pływackie, 103 Unified Process, 162, 532, 703, 725
Total Quality Management, 740 Unified Software Development Process, 49, 166, 703
TP, 497 unikanie usterek, 495
TQM, 740 union(), 414
transformacja modelu, 447, 448, 449 unit testing, 510
dokumentowanie, 475 Universal Modeling Language, 21,43, 63
zarządzanie transformacjami, 475 UNIX, 287
zasady, 453 uprząż testowa, 539
trwałe dane, 303, 309 URPS, 163
Turner C. D.,516 uruchamianie na sucho, 150
tworzenie oprogramowania, 54 uruchamianie projektu, 643
tworzenie systemu, 692 usługi, 267, 270, 321,343
typ klienta, 717 usterki, 494, 499, 500
typed languages, 72 usterki maszyny wirtualnej, 502
typy atrybutów, 404 usterki wykryte podczas inspekcji kodu, 125
typy danych, 72 usterki wykryte podczas testowania, 125
utrzymanie systemu, 54
uwierzytelnianie, 313, 317
U uzgodnienie modelu analitycznego z klientem, 243
U2TP, 539, 540 uzgodnienie umowy projektu, 642
uczestnicy, 43,44, 116, 640 użyteczność, 158, 162, 568
udział w zebraniach zespołu, 147 użytkownik, 124, 239
użytkownik klasy, 403
868 Skorowidz

v dziedziczenie implementacyjne, 360


dziedziczenie specyfikacyjne, 360
vaporware, 37 frameworki aplikacyjne, 381
variants, 602 obiekty aplikacyjne, 359
verdict, 540 obiekty realizacyjne, 359
version, 602 przydzielanie odpowiedzialności, 389
Vlissides J., 283 wzorce projektowe, 364, 383
V-model, 687, 699, 700 zarządzanie wykorzystywaniem gotowych
VMS, 632 rozwiązań, 386
Vodde B., 766 zasada zastępowania Liskov, 364
wielowątkowość, 321
wielozbiory, 411,413
W Większość ma rację, 591
Wigand R. T., 679
waga lekka, 737 WinWin, 590, 592
walkthrough, 495 Władca Pierścieni, 446
warianty, 602, 604, 623 Właściciel ma ostatnie słowo, 591
Warmer J., 407 właściciel produktu, 675
warstwy, 275 Work Breakdown Structure, 640, 646
wartość wypracowana, 667 work packages, 640, 646
warunki graniczne, 304, 323, 345 work product, 640
warunki końcowe, 80, 406, 419 work units, 640
warunki wstępne, 80, 406, 419 workflows, 704
waterfall model, 699 wpadki produkcyjne, 354
WBS, 646, 648, 657 wspieralność, 162
wchłonięcie klasy, 457 wspinaczka wysokogórska, 716
wciągnięcie metod, 450, 451 współdzielenie kodu między warianty, 624
wciągnięcie pola, 450 wstępna deklaracja problemu, 190
wciągnięcie treści konstruktora, 450, 451 wstępne definiowanie architektury systemu, 642
Weber C. V., 723 wstępne zaplanowanie zarządzania projektem, 642
WebifyWatch, 167 Wstępny Plan Zarządzania Projektem
WebObjects, 384 Programistycznym, 642
Weinand A., 382 WWW, 140
Weinberg G. M„ 732 wybór konfiguracji sprzętowej, 306
wersje, 602, 604 wybór liczebności zespołów, 662
weryfikacja modelu analitycznego, 254 wybór platformy, 306
weryfikacja projektu systemu, 326 wybór strategii przechowywania danych, 311
weryfikator, 240, 331 wybór strategii przechowywania obiektów trwałych,
weryfikator racjonalizacji, 587 340
weryfikowalna specyfikacja, 165 wydajność, 162
weryfikowalne cechy specyfikacji, 164 wykaz zaległości produktu, 674
wędrówka po kodzie, 132, 495 wykaz zaległości przebiegu, 674
węzły kontrolne, 101 wykorzystywanie gotowych rozwiązań, 353, 386
whitebox tests, 504 wykres Gantta, 126
wideokonferencje, 140 wykresy wygaszania, 676
widok, 69, 281,368 wykrywanie usterek, 495
widzialność, 405 wymagania, 157
wiele na wiele, 90 wymagania dotyczące interfejsu, 163
wielokrotne wykorzystywanie frameworków, 721 wymagania funkcyjne, 48, 161, 191
wielokrotne wykorzystywanie rozwiązań wymagania implementacyjne, 163
wzorcowych, 353, 359 wymagania jakościowe, 80, 163
ARENA, 390 wymagania operacyjne, 163
biblioteki klas, 383 wymagania pakietowe, 163
delegowanie, 363 wymagania pozafunkcyjne, 48, 162, 163, 182, 191,
dokumentowanie wykorzystania gotowych 205
rozwiązań, 388 wymagania prawne, 163
869 Skorowidz

wyraziste kamienie milowe, 665


wyrównywanie braku kwalifikacji, 662
Z
wysokopoziomowy projekt systemu, 654 zachowanie obiektu, 67
wystąpienia, 72 zachowanie systemu w kategoriach aktywności, 69
wysyłanie komunikatu, 75, 95 zachowanie zewnętrzne, 79
wywiady strukturalne, 141 zadanie, 43, 44, 47, 116, 124, 640, 646
wzorce projektowe, 359, 364, 383,402, 721, 771 pakiet, 126
Abstract Factory, 772 specyfikacja prac, 126
Adapter, 365, 366, 371, 773 zagadnienia, 551, 556, 707
Bridge, 774 zagadnienia wynikowe, 557
Command, 775 zagadnieniowy model cyklu życiowego, 707
Composite, 776 zagnieżdżone maszyny stanów, 100
delegowanie, 364 zagnieżdżony stan, 100
dziedziczenie, 364 założenia kontroli dostępu, 312, 340
Fabryka abstrakcyjna, 376, 390, 772 zamknięta architektura warstwowa, 277
Facade, 777 zamknięte zagadnienie, 707
Fasada, 295, 777 zapora sieciowa, 315
hermetyzacja algorytmów, 780 zarządzanie analizą wymagań, 237
hermetyzacja hierarchii, 378 zarządzanie cyklem życiowym, 683
hermetyzacja kontekstu, 373 zarządzanie danymi, 303
hermetyzacja kosztownych obiektów, 779 zarządzanie emisjami, 616
hermetyzacja platformy, 376, 772 zarządzanie gałęziami, 619
hermetyzacja podsystemów, 777 zarządzanie generowaniem binariów, 601
hermetyzacja przechowywania danych, 368 zarządzanie identyfikowalnością, 187
hermetyzacja przepływu sterowania, 377, 775 zarządzanie jakością oprogramowania, 691
heurystyki pomocne w wyborze wzorców zarządzanie konfiguracją, 56,189, 597, 600
projektowych, 781 agregat CM, 602, 603
heurystyki wyboru wzorców projektowych, 379 agregat zarządzania konfiguracją, 602
Kompozyt, 378, 776 aktywności, 597, 600, 601, 611
Most, 368, 774 aspekty kierownicze, 627
Observer, 778 audyt, 601
Obserwator, 283, 393, 778 biblioteka, 603, 606
oddzielenie encji od widoków, 778 ClearCase, 610, 611
otoczka dla starszego kodu, 773 CVS, 610, 611
podmiana implementacji, 774 dokumentowanie, 627
Polecenie, 377, 392, 775 elementy konfiguracji, 602, 603
Proxy, 308, 457, 779 emisja, 602, 605
rekurencyjna reprezentacja hierarchii, 776 ewolucja elementu konfiguracji, 608
Strategia, 373, 518, 780 gałęzie, 607
heurystyki wspomagające zarządzanie
Strategy, 780
gałęziami, 621
wybór wzorców projektowych, 367
identyfikacja agregatów CM, 613
identyfikacja elementów konfiguracji, 600, 613
X identyfikatory wersji, 606
integracja ciągła, 630
Xli, 276, 278 katalog główny, 603
XP, 725, 731, 737 koncepcje, 602
konfiguracja, 604
Y konflikty, 620, 623
kontrola zmian, 601
Yahoo Groups, 145 linia bazowa, 604
Yourdon E„ 107, 296 narzędzia wspomagające zarządzanie
konfiguracją, 610
numery wersji, 606
Perforce, 610, 611
870 Skorowidz

zarządzanie konfiguracją produkt wewnętrzny, 648


Plan Zarządzania Konfiguracją Oprogramowania, role, 646
627, 628 SPMP, 642, 652
planowanie aktywności, 629 struktura podziału pracy, 640, 646, 648
promocja, 602, 605 uzgodnienie umowy projektu, 642
przestrzeń robocza, 606 WBS, 648
przypisywanie odpowiedzialności, 628 Wstępny Plan Zarządzania Projektem
raportowanie statusu, 601 Programistycznym, 642
RCS, 610 wynik, 640
repozytorium, 603, 606 zadanie, 640, 646
role, 629 zasoby, 640
scalanie gałęzi, 620 zarządzanie projektowaniem obiektów, 425
schematy identyfikowania wersji, 606 dokumentowanie projektowania obiektów, 425
SCMP, 627 przydzielanie odpowiedzialności, 431
testowanie, 630 wykorzystywanie kontraktów w analizie
warianty, 602, 604 wymagań, 432
wersje, 602, 604 zarządzanie projektowaniem systemu, 328
współdzielenie kodu między warianty, 624 zarządzanie promocjami, 615
zarządzanie emisjami, 616 zarządzanie propozycjami zmian, 626
zarządzanie gałęziami, 619 zarządzanie racjonalizacją, 55, 549
zarządzanie generowaniem binariów, 601 aspekty kierownicze, 585
zarządzanie procesem, 601 racjonalizacja, 551
zarządzanie promocjami, 615, 630 zarządzanie ryzykiem, 644, 669
zarządzanie propozycjami zmian, 626 zarządzanie scentralizowane architektonicznie, 655
zarządzanie wariantami, 623 zarządzanie testowaniem, 531
zarządzanie zmianami, 626 zarządzanie transformacjami, 475
zbiory zmian, 608 zarządzanie tworzeniem oprogramowania, 54
zmiany, 608 cykl życiowy oprogramowania, 56
żądanie zmiany, 602, 604 komunikacja, 55
zarządzanie procesem, 601 zarządzanie konfiguracją oprogramowania, 56
zarządzanie projektem, 54, 56, 552, 637, 639, 690 zarządzanie projektem, 56
aktywności, 640, 641, 645, 646 zarządzanie racjonalizacją, 55
aktywności klasycznego zarządzania projektem, zarządzanie wariantami, 623
653 zarządzanie wykorzystywaniem gotowych rozwiązań,
elementy działania, 646 386
faza definicyjna, 642 zarządzanie zbieraniem wymagań, 183
faza koncepcyjna, 642 zarządzanie zmianami, 626
faza końcowa, 644 zasada zastępowania Liskov, 364, 394
faza startowa, 643 zasoby, 43, 47, 640
faza ustalona, 643 zastoje, 284
funkcje projektowe, 646 zbieranie wymagań, 50, 65, 157, 159, 552
harmonogram, 640, 646 aktywności, 160,166
jednostki pracy, 640 analiza wymagań, 159
koncepcje, 646 dokument analizy wymagań, 188
kontrola projektu, 641, 665 dokumentowanie, 188
kończenie projektu, 641, 671 doskonalenie przypadków użycia, 160, 173
macierz kwalifikacji, 650 identyfikacja aktorów, 160, 167
model zadań, 640, 646, 649 identyfikacja przypadków użycia, 160, 171
modele menedżerskie, 640 identyfikacja relacji między aktorami
organizowanie projektu, 641, 659 a przypadkami użycia, 176
pakiety pracy, 640, 646 identyfikacja relacji między przypadkami użycia,
Plan Zarządzania Projektem, 651 160
planowanie projektu, 641, 654 identyfikacja scenariuszy, 160, 169
praca, 640 identyfikacja wymagań pozafunkcyjnych, 160,182
produkt, 640, 646 inżynieria interfejsu, 165, 166
produkt finalny, 640, 648 inżynieria pierwotna, 165
Skorowidz 871

inżynieria wtórna, 165 zespoły podsystemów, 146


JAD, 185 zespoły trzyosobowe, 663
jednoznaczność, 164 zespół (Serum), 675
kompletność, 164 zintegrowane środowisko programistyczne, 280
komunikacja, 158 zintegrowanie racjonalizacji z modelami systemu, 553
koncepcje, 161 złącza, 270
negocjowanie specyfikacji z klientem, 185 złączenia, 102
początkowa identyfikacja obiektów modelu złudzenie optyczne, 212
analitycznego, 179 zmiany, 37, 608
poprawność, 164 zmiany w implementacji, 367
RAD, 188 zmiany w środowisku operacyjnym, 324
specyfikacja wymagań, 159 zmiany w zakresie dziedziny aplikacyjnej, 368
spójność, 164 zmodyfikowane testowanie kanapkowe, 523
system ARENA, 190 znacznikowane komentarze Javadoc, 429
weryfikowalne cechy specyfikacji, 164 zorientowane obiektowo tworzenie
wymagania funkcyjne, 161 oprogramowania, 41
wymagania pozafunkcyjne, 162 zorientowanie obiektowe, 40
zarządzanie identyfikowalnością, 187 zrównoleglone projekty, 623
zarządzanie zbieraniem wymagań, 183 Z-Schematy, 72
zbiory, 411, 413 ; zwinna realizacja projektu, 673
zbiory zmian, 608 j planowanie projektu, 674
zdalne wywoływania procedur, 283 zwinne metodologie, 725, 737
zdarzenia, 75, 76 '
zdobycie K2, 714
zebrania, 147, 665 Ź
zebrania planistyczne, 674
źle zlokalizowane atrybuty, 456
zebrania początkujące, 118
zebrania statusowe, 117, 118, 665, 675
zebranie otwierające, 664 Ż
zebranie startowe, 674
zespoły, 119, 663 żądanie wyjaśnień, 117,136
zespoły czteroosobowe, 663 żądanie zmian, 117, 137,138, 602, 604
zespoły międzyfunkcyjne, 121,146
872 Skorowidz
Wzorce projektowe
A d a p t e r (360 773) — h e r m e t y z u j e f r a g m e n t istniejącego k o d u , w celu u m o ż l i w i e n i a jego w s p ó ł d z i a ł a -
n i a z n i e k o m p atyb i l n y m ś r o d o w i s k i e m . P r o w a d z i t o d o m i n i m a l i z o w a n i a w p ł y w u „starego" k o d u
na nowe komponenty.
Fabryka abstrakcyjna (376, 390, 722) — s e p a r u j e system o d klas specyficznych dla k o n k r e t n e j p l a t f o r m y ,
na przykład konkretnego systemu okienkowego lub konkretnego systemu operacyjnego.
F a s a d a (295, 777) — r e d u k u j e s p r z ę ż e n i e m i ę d z y k l a s a m i , p o p r z e z z a m k n i ę c i e p o d s y s t e m u w r a m y
prostego interfejsu.
K o m p o z y t (378, 776) — r e p r e z e n t u j e hierarchiczną s t r u k t u r ę o d o w o l n e j szerokości i g ł ę b o k o ś c i . U s ł u g i
z w i ą z a n e z tą h i e r a r c h i ą w y k o r z y s t u j ą relację d z i e d z i c z e n i a , co u m o ż l i w i a j e d n o l i t e t r a k t o w a n i e
w ę z ł ó w k o ń c o w y c h („liści") i w ę z ł ó w p o ś r e d n i c h .
M o s t (369, 774) — s e p a r u j e interfejs klasy o d jego k o n k r e t n e j i m p l e m e n t a c j i . W przeciwieństwie d o w z o r -
ca p r o j e k t o w e g o A d a p t e r , p r o g r a m i s t a n i e jest w ż a d e n s p o s ó b o g r a n i c z a n y p r z e z i s t n i e j ą c y k o d .
O b s e r w a t o r (283, 393, 778) — u m o ż l i w i a u t r z y m y w a n i e s p ó j n o ś c i s t a n u m i ę d z y p o j e d y n c z y m publ i -
katorem a w i e l o m a subskrybentami.
P o l e c e n i e (377, 392, 775) — h e r m e t y z u j e o b i e k t y sterujące, co u m o ż l i w i a j e d n o l i t e t r a k t o w a n i e wszyst-
kich p o l e c e ń u ż y t k o w n i k a , niezależnie o d ich specyfiki. W s p o m n i a n e o b i e k t y n i e w y m a g a j ą dzięki
temu zmian w przypadku dodawania nowych elementów funkcjonalnych.
P r o x y (317, 457, 779) — z w i ę k s z a b e z p i e c z e ń s t w o i w y d a j n o ś ć s y s t e m u p o p r z e z o p ó ź n i a n i e k o s z t o w -
nych obliczeń, optymalizowanie wykorzystania pamięci oraz kontrolę u p r a w n i e ń dostępu d o
obiektu przed załadowaniem go d o pamięci.
S t r a t e g i a (373, 518, 780) — u m o ż l i w i a o d d z i e l e n i e z a s a d o d r e a l i z u j ą c y c h je m e c h a n i z m ó w o r a z o d -
dzielenie k o n t e k s t u o d z b i o r u i m p l e m e n t a c j i , o w e m e c h a n i z m y i i m p l e m e n t a c j e m o g ą być dzięki
t e m u z m i e n i a n e d y n a m i c z n i e w czasie d z i a ł a n i a p r o g r a m u , w s p o s ó b n i e z a u w a ż a l n y dla k l i e n t a .

Notacje
D i a g r a m U M L a k t y w n o ś c i (68, 101) — r e p r e z e n t u j e z a c h o w a n i e g r u p y o b i e k t ó w w kategoriach ich ak-
tywności i z m i a n s t a n ó w .
D i a g r a m U M L klas (65, 86) — r e p r e z e n t u j e s t r u k t u r ę s y s t e m u w kategoriach p o d s y s t e m ó w , klas, atrybu-
tów, operacji i skojarzeń.
D i a g r a m U M L p r z y p a d k ó w u ż y c i a (65, 79) — r e p r e z e n t u j e f u n k c j o n a l n o ś ć s y s t e m u z p e r s p e k t y w y
aktora.
D i a g r a m U M L s e k w e n c j i (67, 95) — r e p r e z e n t u j e z a c h o w a n i e j a k o ciąg i n t e r a k c j i m i ę d z y o b i e k t a m i
określonej grupy.
D i a g r a m U M L s t a n ó w (67, 98) — r e p r e z e n t u j e z a c h o w a n i e p o j e d y n c z e g o o b i e k t u w kategoriach s t a n ó w
i przejść a u t o m a t u skończonego.
D i a g r a m w d r a ż a n i a U M L (304) — r e p r e z e n t u j e o d w z o r o w a n i e k o m p o n e n t ó w p r o g r a m u w w ę z ł y
sprzętowe.
G r a f P E R T (126) — r e p r e z e n t u j e p o d z i a ł p r a c y n a z a d a n i a i u w a r u n k o w a n i e c z a s o w e m i ę d z y t y m i
zadaniami.
M o d e l e z a g a d n i e ń (562) — r e p r e z e n t u j ą u z a s a d n i e n i e p o d e j m o w a n y c h d e c y z j i w k a t e g o r i a c h z a g a d -
nień, propozycji, a r g u m e n t ó w , kryteriów i rozstrzygnięć.
Przykładowe systemy
2 B W a t c h i SatWatch (161) 2BWatch jest zegarkiem c y f r o w y m p o s i a d a j ą c y m d w a przyciski. SatWatch
jest z e g a r k i e m s y n c h r o n i z u j ą c y m swe wskazanie z lokalną strefą czasową za p o m o c ą s y s t e m u
GPS, nie p o s i a d a j ą c y m ż a d n e j możliwości s t e r o w a n i a p r z e z u ż y t k o w n i k a — u ż y t k o w n i k m o ż e
jedynie odczytywać w s k a z a n i e czasu. O b a te systemy są proste, lecz wystarczająco d o w p r o w a -
dzenia w k o n c e p c j ę zbierania w y m a g a ń .
A R E N A (190, 245, 334, 390, 433, 478) to wieloużytkowy system webowy służący organizowaniu i rozgry-
w a n i u t u r n i e j ó w . System niezależny jest o d k o n k r e t n e j gry w t y m sensie, że jej autorzy, p o przy-
s t o s o w a n i u jej interfejsu d o w y m o g ó w systemu, m o g ą wysłać tę grę n a serwer i n a t y c h m i a s t zor-
g a n i z o w a ć turniej, o b e j m u j ą c y graczy i kibiców r o z p r o s z o n y c h w internecie. O r g a n i z a t o r z y
m o g ą wykorzystywać istniejące i d e f i n i o w a ć n o w e style rozgrywek, różniące się z a s a d a m i przy-
działu graczy d o poszczególnych meczów oraz decydowania o zwyc i ę s twi e. Elementem systemu są
m e c h a n i z m y r e k l a m o w e , u m o ż l i w i a j ą c e o p e r a t o r o m s y s t e m u r e k o m p e n s o w a n i e k o s z t ó w dzia-
łalności, poprzez płatne wyświetlanie b a n e r ó w dostarczonych przez zainteresowanych sponsorów.
System A R E N A zrealizowany został we w s p ó ł p r a c y U n i w e r s y t e t u T e c h n i c z n e g o w M o n a c h i u m
i U n i w e r s y t e t u w O t a g o w N o w e j Zelandii w latach 2002 i 2003.
C T C (568) p o w s t a ł w związku z m o d e r n i z a c j ą k o m u n i k a c j i miejskiej w Stuttgartcie, polegającą n a
s t o p n i o w y m z a s t ę p o w a n i u t r a m w a j ó w koleją miejską. U m o ż l i w i a scentralizowane s t e r o w a n i e
r u c h e m z p o z i o m u licznych nastawni, kontrolujących poszczególne sekcje komunikacyjne. W tej
książce wykorzystaliśmy m e c h a n i z m y k o n t r o l i d o s t ę p u o b o w i ą z u j ą c e w t y m systemie, j a k o ilu-
strację m e t o d z a r z ą d z a n i a racjonalizacją.
FRIEND (167, 219) to rozproszony system zarządzania sytuacjami kryzysowymi. Umożliwia f u n k c j o n a -
riuszom w terenie, d y s p o zyt o r o wi i a d m i n i s t r a c j i f e d e r a l n e j k o m u n i k o w a n i e się w celu przy-
działu z a s o b ó w d o obsługi i n c y d e n t u . System FRIEND jest n i e p o r ó w n a n i e bardziej z ł o ż o n y niż
systemy 2BWatch i SatWatch; jest o n p r z y k ł a d e m tego, jak zbieranie w y m a g a ń i m e t o d y ich a n a -
lizy skalują się d o rzeczywistych sytuacji. Z r e a l i z o w a n y został n a Uniwersytecie Carnegie M e l l o n
w latach 1992 - 1994.
m y C a r P a r t s (611) to w e b o w y katalog s a m o c h o d o w y c h części z a m i e n n y c h . U m o ż l i w i a przeglądanie,
w y s z u k i w a n i e i z a m a w i a n i e o k r e ś l o n y c h części z a r ó w n o w s p o s ó b u p r o s z c z o n y , p r z y d a t n y dla
nowicjuszy, jak i w s p o s ó b ekspercki, p r z y d a t n y p r o f e s j o n a l n y m m e c h a n i k o m . W y k o r z y s t a l i ś m y
t e n system j a k o ilustrację m e t o d a zarządzania k o n f i g u r a c j ą . System myCarParts z a i n s p i r o w a n y
został s y s t e m e m P A I D , r e a l i z o w a n y m we w s p ó ł p r a c y U n i w e r s y t e t u C a r n e g i e Mellon i U n i w e r -
sytetu T e c h n i c z n e g o w M o n a c h i u m w latach 1998 i 1999.
m y T r i p (288) jest s y s t e m e m w s p o m a g a j ą c y m p a n o w a n i e p o d r ó ż y i u m o ż l i w i a j ą c y m p o b i e r a n i e zdal-
nie z a p a m i ę t a n y c h tras d o k o m p u t e r a p o k ł a d o w e g o s a m o c h o d u , k t ó r y to k o m p u t e r p r o w a d z i
n a s t ę p n i e kierowcę d o celu. System ten jest w y n i k i e m inspiracji s y s t e m e m JAMES, k t ó r y realizo-
w a n y był we w s p ó ł p r a c y U n i w e r s y t e t u C a r n e g i e M e l l o n i U n i w e r s y t e t u T e c h n i c z n e g o w M o n a -
c h i u m w latach 1997 i 1998. System myTrip w y k o r z y s t a l i ś m y n a p o t r z e b y zilustrowania m e t o d
p r o j e k t o w a n i a systemu.

You might also like