Istnieje wiele sposobów projektowania aplikacji, a na ten temat powstały liczne opracowań. Oto pierwszy odcinek prostego kursu UML-a, który sam w sobie nie jest sposobem projektowania, a sposobem na przejrzystą prezentację projektu.
Tworzenie aplikacji, niezależnie od tego, czy jest
to aplikacja działająca po stronie serwera, czy
też standardowy program, nie powinno być
procesem bezmyślnym. Niestety często programiści
zaraz po otrzymaniu zlecenia napisania programu
siadają do ulubionego edytora i rozpoczynają kodowanie.
O ile w przypadku bardzo małych i prostych
aplikacji takie podejście może się sprawdzić, o tyle
w przypadku nieco już większych może to być
początek problemów i jednocześnie początek końca
tak wspaniale zapowiadającej się aplikacji…
Istnieje wiele sposobów projektowania aplikacji.
Powstało na ten temat także wiele poważanych
opracowań.
UML sam w sobie nie jest sposobem projektowania,
a sposobem na przejrzystą prezentację
projektu. Jednak tworząc diagramy UML, będziesz
zmuszony zastanowić się nad budową aplikacji,
czyli tak naprawdę ją zaprojektować. Co więcej,
budowa diagramów UML pozwala często na
wychwycenie problemów projektowych, które normalnie
wypłynęłyby podczas kodowania, znacznie
utrudniając pracę, bądź też powodując konieczność
przeprojektowania zakodowanej już aplikacji.
Czym jest UML
UML (ang. Unified Modeling Language) jest graficznym
językiem pozwalającym na opisywanie systemów
(nie tylko informatycznych, lecz głównie do tego celu
jest on używany) za pomocą diagramów i słów. UML
jest standardem (Unified – zunifikowany, jednolity),
dzięki czemu diagramy w nim zbudowane będą
zrozumiałe dla każdego, kto miał z nim styczność.
Język ten jest bardzo rozbudowany, szczególnie
jego najnowsze wersje (2.0 i wyższe).
Na szczęście w większości przypadków używa się tylko jego
drobnego podzbioru. Nawet w naszej serii artykułów
nie sposób będzie zaprezentować wszystkie
możliwości drzemiące w UML-u. Postaramy się
zaprezentować jednak to, co najważniejsze. Reszty
w razie potrzeby będziesz mógł się nauczyć sam,
czy to z materiałów dostępnych w Internecie, czy
też z dostępnej literatury (patrz ramka Literatura).
Narzędzia
Diagramy UML możesz z powodzeniem
rozrysowywać na zwykłej kartce. Największą
zaletą takiego podejścia są koszty – nie musisz kupować żadnego komercyjnego oprogramowania.
Jednak ciągłe używanie ołówka i gumki
szybko okaże się mało wygodne. Wówczas warto
rozejrzeć się za odpowiednią aplikacją. Tutaj
wybór jest olbrzymi, od programów darmowych,
aż po te komercyjne, kosztujące niemałe
pieniądze, ale i oferujące adekwatne do ceny
możliwości.
Z programów darmowych warto przyjrzeć się:
-
ArgoUML (http://argouml.tigris.org) – jest to
aplikacja napisana w Javie, a więc działająca
pod niemal każdym systemem operacyjnym. Jej
wadą jest jednak wsparcie jedynie dla UML-a w
wersji 1.4, co uniemożliwi budowę niektórych
diagramów. - Poseidon for UML (http://www.gentleware.com)
– produkt komercyjny, tak jak ArgoUML stworzony
w Javaie. Oferujący jednak wsparcie dla
UML-a w wersji 2.0. Istnieje kilka jego wersji,
w tym Community Edition, która jest darmowa
i doskonale nadaje się do nauki.
Jeśli interesują cię programy płatne, wówczas
wybór jest olbrzymi. Oto kilka propozycji: - Poseidon for UML (http://www.gentleware.com) – tak jak wspomniałem wcześnie,
istnieje kilka wersji tego programu, wśród
nich większość to wersje komercyjne (tak
naprawdę wszystkie poza Community Edition
są płatne). - IBM Rational Software (http://www.ibm.com/software/rational) – cała rodzina programów,
wśród których znajduje się także aplikacja
do budowy diagramów UML. Jest to produkt
z najwyższej półki, o czym niestety nie pozwala
zapomnieć także jego cena. - Sparx Enterprise Architect (http://www.sparxsystems.com.au) – bardzo dobry program,
oferujący wsparcie dla UML-a w wersji 2.1,
dodatkowo pozwalający budować diagramy baz
danych. A to wszystko za dość rozsądną cenę
(w przypadku jednego stanowiska, od 135$ za
wersję Desktop Edition).
Wszystkie komercyjne programy oferują możliwość
generowania kodu programu na podstawie
diagramów UML, a także generowanie diagramów
na podstawie kodu (Reverse Engineering – inżynieria
zwrotna).
Diagramy, przedstawiane w artykułach, pochodzić
będą z programu Sparx Enterprise Architect.
Czym kurs ten jest, a czym nie
jest, czyli obietnice i wymagania
Zakładam, że czytając ten kurs, posiadasz
przynajmniej podstawowe pojęcie na temat
programowania obiektowego i nie są ci obce takie
terminy jak:
- klasa i klasa abstrakcyjna,
- obiekt,
- interfejs,
- dziedziczenie,
- implementowanie
i inne związane z programowaniem obiektowym.
Jeśli masz z tym problem, masz jeszcze trochę
czasu do nadrobienia zaległości – w końcu kolejny
odcinek za dwa miesiące.
Kurs ten nie nauczy cię obsługi żadnego programu
pozwalającego na budowanie diagramów
UML. Jego celem jest przedstawienie sposobu
czytania oraz projektowania podstawowych diagramów.
Program jest tu tylko narzędziem i każdy
może wybrać taki, jaki mu najbardziej odpowiada.
Na rysunku 2 możesz zobaczyć kilka przykładów
tej samej klasy z różnymi poziomami
widoczności.
Nazwy operacji mogą wyglądać następująco:
[widoczność] nazwa [(parametry)] [: typ
wartości zwracanej] [{ustawienia}]
gdzie parametry:
nazwa [: typa parametru]
A więc poprawne nazwy operacji to:
pobierzImie
+pobierzImie
+pobierzImie()
+pobierzImie(): string
+ustawImie(imie: string): void
A także:
pobierzImie: string
pobierszImie(): sring
Na rysunku 3 możesz zobaczyć kilka przykładów
operacji klas zaprezentowanych przy różnych
ustawieniach widoczności.
Klasy abstrakcyjne
Zapewne wiesz, czym jest klasa abstrakcyjna.
Dla przypomnienia: jest to klasa, która posiada
jedną lub więcej metod (w UML-u nazwanych
operacjami) nieposiadających ciała. Metody takie
są jedynie deklaracjami, zapowiedziami, że klasa
dziedzicząca (o dziedziczeniu będzie w dalszej
części tego odcinka) po klasie abstrakcyjnej
dostarczy ciała takiej metody, w przeciwnym razie
sama też będzie klasą abstrakcyjną. Ważne jest
to, że nie można tworzyć instancji (obiektów) klas
abstrakcyjnych.
W UML-u klasy abstrakcyjne niewiele różnią się
od normalnych klas. Jedyną widoczną różnica jest
ich nazwa, napisana kursywą (pochyłą czcionką)
– zobacz rysunek 4.
Warto zauważyć, że w UML-u można tworzyć
także operacje abstrakcyjne. Wówczas ich nazwa
będzie tak jak nazwa klas abstrakcyjnych, napisana
kursywą.
Agregacja
Agregacja najprościej mówiąc oznacza zawieranie.
Na przykład:
- lampka zawiera żarówkę,
- komputer zawiera procesor,
- jabłko zawiera robaka,
- itd.
Agregację na diagramie UML oznacza się
pustym rombem, tak jak zaprezentowano to na rysunku
5. Przyjrzy się także klasom biorącym udział
w agregacji – jak widzisz, z agregacji korzystałeś
już zapewne wiele razy.
Kompozycja
Szczególnym przypadkiem agregacji jest
kompozycja. Od agregacji różni się tym, że klasa
posiada obiekty (składa się z obiektów), które bez
tej klasy istnieć by nie mogły. Przykłady takich
związków to:
- blok zawiera mieszkania (mieszkania poza
blokiem nie istnieją), - komputer zawiera procesor,
- łazienka zawiera wannę,
- itd.
Kompozycja na diagramie UML oznaczona jest
wypełnionym rombem – tak jak na rysunku 6.
Zaczynamy
Przyjrzymy się klasom, wchodzącym w skład
diagramów klas. Diagramami klas będziemy się
zajmowali przez jakiś czas. Jakie zadania spełniają
klasy? Są on najczęściej swoistym słownikiem
opisującym świat modelowanego systemu. Np.
w przypadku forum internetowego mogłyby istnieć
takie klasy jak Użytkownik, Post, Dział, Temat
i inne.
Przyjęło się, że nazwa klasy opisuje zadanie
jakie klasa ma spełniać (np. Użytkownik mówi, że
klasa odpowiada za obsługę użytkownika, warto
się zastanowić, czy lepszą nazwą w przypadku
forum nie byłoby {stala}KontoUżytkownika{/stala}). Nazwa klasy
zawsze rozpoczyna się wielką literą, w przypadku
zaś gdy jest to nazwa złożona z kilku wyrazów,
każdy wyraz rozpoczyna się bezpośrednio po
poprzednim (bez spacji, odstępu czy innego znaku)
wielką literą (patrz KontoUżytkownika).
Klasy mogą posiadać atrybuty oraz operacje.
Atrybuty można sobie wyobrazić jako pojemniki
służące do przechowywania danych, a więc są
odpowiednikami zmiennych (pól klas) z języków
programowania. Operację są czynnościami, jakie
może wykonywać klasa – są to więc odpowiedniki
metod klas z języków programowania.
Nazwy atrybutów i operacji tworzy się w sposób
analogiczny do nazw klas, z tą różnicą, że
powinny one rozpoczynać się od małej litery. Na
rysunku 1 możesz zobaczyć kilka notacji prezentujących
klasę.
Jak widzisz, klasa reprezentowana jest przez
prostokąt podzielony na bloki. Górny blok zawsze
zawiera nawę klasy (na rysunku {stala}NazwaKlasy{/stala}).
Kolejne bloki zawierają odpowiednio pola oraz
atrybuty. Dodatkowo klasa może posiadać jeszcze
dodatkowe pola z innymi informacjami.
W zależności od ustawionego poziomu
szczegółowości, widoczne mogą być wszystkie
bądź tylko niektóre bloki. Jak przedstawiono to na
rysunku 1, najmniej szczegółowa prezentacja klasy
posiada tylko jej nazwę.
Pamiętaj, że wszystkie te poziomy szczegółowości
są poprawne. Możesz się między nimi
przełączać w zależności od konieczności – np.
w zależności od tego, komu chcesz zaprezentować
diagram. Warto także pamiętać, że nie każdy program
potrafi przełączać się pomiędzy wszystkimi
poziomami. Enterprose Architect ma na przykład
problemy z poziomem pierwszym (sama nazwa
klasy – po wyłączeniu prezentacji wszystkich pozostałych
bloków, wyświetla on blok z nazwą oraz
drugi, pusty blok).
Diagramy na rysunku zawierają napisy
w poszczególnych polach w różnych kolorach.
Pamiętaj że kolory nie mają tu znaczenia. Enterprse
Architect koloruje zawartość odpowiednich
pól w celu czytelniejszej ich prezentacji. Często
jednak wszystkie elementy diagramów są
czarno-białe.
Zarówno atrybutom, jak i operacjom można przypisać poziomy widoczności:
- publiczny – dostęp do elementu mają obiekty wszystkich innych klas systemu,
- prywatny – dostęp do elementu mają tylko obiekty klasy, w której element jest zdefiniowany,
- chroniony – dostęp do elementu mają obiekty klasy, w której element jest zdefiniowany oraz klas dziedziczących po tej klasie.
Widzisz tutaj analogię do działania modyfikatorów
dostępu w klasach obiektowych języków
programowania (jak public, private i protected
z języków PHP, Java czy C++)?
Na diagramach klas UML elementy z określoną widocznością posiadają nazwy poprzedzone odpowiednim znakiem:
- + – element publiczny,
- – (dywiz) – element prywatny,
- # – element chroniony.
Istnieje jeszcze jeden, rzadko używany (dostępny
np. w Javie) poziom dostępu, określający widoczność
dla pakietu, a oznaczany znakiem tyldy ~. Na
rysunku 1 widać zdefiniowane typy atrybutów oraz
parametry i wartości zwracane przez operacje.
Nazwa atrybutów, w zależności od ustawienia
poziomu prezentacji szczegółów w programie,
może mieć następującą postać:
[widoczność] [/] nazwa [: typ]
[wielokrotność] [ = wartość domyślna]
[{ustawienia}]
To co zapisano w nawiasach kwadratowych,
to dane opcjonalne, których nie trzeba podawać
i które można ukrywać. Tak więc poprawne nazwy
atrybutów mogą przyjmować postać:
imieKlienta
+imieKlienta
-imieKlienta: string
#imieKlienta: string = \"Jan\"
+imieKlienta: string = \"Jan\" {readOnly}
Ale także:
imieKlienta = \"Jan\"
klienci[1..10]
i inne.
Asocjacja
Asocjacja jest podstawowym rodzajem
związków między klasami. Oznacza ona istnienie
trwałego powiązania pomiędzy nimi. Oto przykłady
asocjacji
- firma zatrudnia pracowników,
- student studiuje na uczelni,
- ekspedientka pracuje w sklepie,
- itd.
Przykłady asocjacji przedstawia rysunek 7.
Asocjacja może być jednokierunkowa, to znaczy
można zaznaczyć jej kierunek – wiadomo, że
firma zatrudnia pracowników, ale nie odwrotnie.
Kierunek asocjacji zaznacza się strzałką – zobacz
rysunek 8. Asocjacja taka nazywana jest
nawigacją.
Asocjacja n-arne
Zaprezentowana wyżej asocjacja to tak zwana
asocjacja binarna – są to asocjacje, w skład
których wchodzą dwie klasy. Czasami (jednak
o wiele rzadziej) stosuje się asocjacje n-arne.
w skład takich asocjacji może wchodzić więcej
klas. Oto przykład: w sklepie oferującym części
komputerowe mogą wystąpić następujące klasy
połączone w asocjację n-arną: Część, Cennik,
Cena. Na rysunku 9 pokazano diagram takiej
właśnie asocjacji. Pamiętaj, że w jej skład może
wchodzić więcej niż trzy klasy.
Nazwy, role i liczebność asocjacji
Omawiając asocjację, trzeba wspomnieć
także o ich nazwach, rolach i liczebności. Nazwa
asocjacji wskazuje bezpośrednio czynność, jaka
zachodzi pomiędzy klasami. Może także wskazywać
kierunek – zobacz rysunek 10.
Role określają, jak sama nazwa wskazuje,
jaką rolę pełni dana klasa w asocjacji – zobacz
rysunek 11.
Bardziej dokładnego wyjaśnienia wymaga
liczebność (mimo że w gruncie rzeczy jest ona
bardzo prostą sprawą). Liczebność określa, ile
obiektów jednej klasy bierze udział w asocjacji drugiej
klasy.
Liczebność można określi stałą cyfrą, np.:
- 1,
- 2,
- 10,
- itd.
Można określić liczebność jako nieskończoność
– *. Można także określić przedział liczebności, np.:
- 0..3 – od 0 do 3
- 1..5 – od 1 do 5
- 10..100 – od 10 do 100
- 1..* – od 1 do nieskończoności (czyli minimum 1)
- itp.
Kolejnym sposobem jest złożone określanie
liczebności:
- 1, 3, 7..20, 23, 30,
- 1..5, 7, 9
- itp.
Odpowiedni przykład pokazano na rysunku 12.
Generalizacja
Generalizacja odpowiada dziedziczeniu znanemu
z języków programowania. Inaczej mówiąc,
jest to związek pomiędzy bardziej ogólną klasą
(rodzicem) a klasą bardziej szczegółową (rodzicem).
Oto przykłady generalizacji:
- manager jest pracownikiem,
- sklep jest firmą,
- latarnia jest lampą, podobnie lampą jest lampka nocna,
- kot jest zwierzęciem,
- itd.
Przykłady generalizacji znajdują się na rys. 13.
Zakończenie
I wiesz już dość dużo. W kolejnym odcinku
dowiesz się czym są i jak deklarować interfejsy. Ale
nie zdradzajmy wszystkiego. Tymczasem przeczytaj
artykuł kilka razy i spróbuj samodzielnie przygotować
różne diagramy UML.