Diagram klas jest jednym z najczęściej stosowanych diagramów UML. Opisuje aspekty statyczne systemu. Przedstawia deklaratywne elementy dziedziny problemowej oraz związki między nimi. Elementy dziedziny problemowej modelowane są za pomocą klas i interfejsów, a kooperacja między nimi za pomocą związków. W ten sposób powstaje model struktury systemu, będący podstawą dla jego konstrukcji. Kompletny model struktury złożonego systemu może składać się z wielu diagramów klas. Grupowanie klas w ramach jednego diagramu jest zależne od decyzji analityka lub projektanta. Jeden diagram klas może np. zawierać elementy systemu biorące udział w realizacji danego przypadku użycia.
Ze względu na pożądany poziom szczegółowości modelu istnieją dwa poziomy tworzenia diagramu klas:
- poziom konceptualny i
- poziom implementacyjny.
Klasa
Klasa jest uogólnieniem zbioru obiektów, które mają takie same atrybuty, operacje, związki i znaczenie. Innymi słowy, klasa stanowi wzorzec, na podstawie którego tworzone są obiekty. Każda klasa zawiera zestaw informacji istotnych z punktu widzenia kontekstu systemu.
Z klasą związane są przede wszystkim takie pojęcia jak atrybuty i operacje. Atrybut określa właściwość obiektu przyjmującą wartość danego typu. Zestaw atrybutów stanowi charakterystykę statyczną obiektów wywodzących się z danej klasy. Operacja jest funkcją realizowaną przez obiekt. Zestaw operacji definiuje zachowanie obiektów w systemie. Operacja ma sygnaturę, która określa jej dopuszczalne parametry, sposób wywołania i zwracania wyników działania. Operacje klasy działają na pojedynczych obiektach lub zbiorach obiektów. W odróżnieniu do atrybutów, zwykle tworzona jest jedna instancja operacji klasy, przechowywana w klasie i będąca wspólna dla wszystkich obiektów tej klasy. W szczególnych przypadkach, kiedy dany atrybut lub operacja może istnieć niezależnie od obiektu, często lepiej jest zamodelować nową klasę (z własnym zestawem atrybutów i operacji), przedstawiającą kontekst rozpatrywanego atrybutu lub operacji, np. klasa Adres lub SterowanieKomunikacją.
Domyślnie graficzny opis klasy jest przedstawiony jako prostokąt podzielony na trzy sekcje:
- nazwa klasy (reprezentująca zbiór obiektów),
- zestaw atrybutów (opisujący cechy statyczne obiektów wywodzących się z klasy),
- zestaw operacji (opisujący zachowanie obiektów wywodzących się z klasy).
W zależności od tego jaki etap analizy/projektowania systemu jest realizowany oraz jaki zakres „widoczności” diagram klas powinien prezentować, prostokąt reprezentujący klasę na diagramie może zawierać:
- samą nazwę klasy (interesuje nas tylko wyszczególnienie elementów modelowanej dziedziny problemowej);
- nazwę klasy oraz sekcję zestawu atrybutów (oprócz wyszczególnienia elementów dziedziny problemowej interesują nas także ich cechy statyczne);
- nazwę klasy oraz zestaw operacji (oprócz wyszczególnienia elementów dziedziny problemowej interesują nas również ich zachowania);
- nazwę klasy oraz zestaw atrybutów i zestaw operacji (interesuje nas zarówno wyszczególnienie elementów dziedziny problemowej jak i ich cechy statyczne i zachowania).
Poza tym, sama nazwa klasy umieszczona w prostokącie bez podziału na sekcje może oznaczać, że atrybuty i operacje zostały wyspecyfikowane, ale nie są w sposób jawny umieszczone na diagramie (nie są istotne z punktu widzenia przeznaczenia takiego diagramu klas). Klasa może być również przedstawiona jako prostokąt z trzeba sekcjami, z których wyspecyfikowana jest jedynie nazwa klasy. W przypadku, gdy liczba atrybutów lub operacji jest znacząco duża, ich wyliczenie w odpowiednich sekcjach można przerwać wielokropkiem. Oznacza to, że w klasie znajdują się jeszcze inne atrybuty lub operacje oprócz tych wymienionych w odpowiednich sekcjach.
Notacja dopuszcza także stosowanie większej liczby sekcji w klasie. Dodatkowe sekcje mogą być związane z np. zobowiązaniami lub wyjątkami. Stosowanie większej liczby sekcji jest uzależnione przede wszystkim od poziomu szczegółowości jaki diagram ma przedstawiać. Należy jednak pamiętać, że może to negatywnie wpływać na przejrzystość i czytelność modelu.
Jak już wspomniano, specyfikacja klasy może być wzbogacona również o zobowiązania. Jest to technika opcjonalna i stosowana raczej pomocniczo w celu dostarczenia wysokopoziomowego opisu zachowania klas. Na tej podstawie nie jest wymagane koncentrowanie się na poszczególnych atrybutach i operacjach klasy – odbiorca modelu na podstawie zobowiązań może określić przeznaczenie danej klasy. Zobowiązania są pewnego rodzaju kontraktem klasy, jej odpowiedzialnością. Definiuje się je przeważnie na podstawie wyników modelowania biznesowego lub na poziomie analizy wymagań. Zobowiązania najczęściej wykorzystuje się w celu wypracowania koncepcji realizacji scenariuszy przypadków użycia. Zobowiązania są umieszczane w klasie jako dodatkowa sekcja. Klasa, która składa się tylko z sekcji zobowiązań i listą współpracujących klas, nazywana jest kartą CRC (Class-Responsibility-Collaboration card).
Jedną z cech opisujących atrybuty i operacje klasy jest ich widoczność. Widoczność określa dostęp do atrybutów i operacji wewnątrz jednej klasy przez obiekty innych klas. Wyróżnione są cztery poziomy widoczności:
- publiczny (+) – dostęp do atrybutu lub operacji mają obiekty wszystkich klas;
- prywatny (-) – dostęp do atrybutu lub operacji mają obiekty danej klasy;
- chroniony (#) – dostęp do atrybutu lub operacji mają obiekty klas dziedziczących z danej klasy;
- pakietowy (~) – dostęp do atrybutu lub operacji mają obiekty klas pochodzących z tego samego pakietu.
Dobrym zwyczajem jest nadawanie domyślnie atrybutom widoczności prywatnej. Dostęp do tych atrybutów jest wtedy regulowany specjalnymi publicznymi operacjami. Zmniejsza się wtedy ryzyko utraty kontroli nad niepowołanym dostępem do atrybutów.
Atrybuty i operacje klasy mogą być indywidualne dla każdego obiektu danej klasy lub też stałe (takie same, niezmienne) we wszystkich obiektach klasy. Te pierwsze są nazywane atrybutami lub operacjami egzemplarzowymi. Drugi przypadek oznacza atrybuty lub operacje statyczne. Statyczność oznacza, że np. zmiana wartości atrybutu w jednym obiekcie jest automatycznie wprowadzana w innych obiektach tej samej klasy.
Nazwy klas, atrybutów i operacji są różne w przypadku diagramów klas na poziomie konceptualnym i implementacyjnym. Diagram klas na poziomie konceptualnym powinien zawierać nazewnictwo proste i jasne w zrozumieniu. Zarówno klasy jak i jej atrybuty oraz operacje są definiowane za pomocą wyrażeń rzeczownikowych, np.:
- klasa: Zamówienie Klienta;
- atrybut: Numer Zamówienia;
- operacja: Dodaj Produkt Do Zamówienia.
- klasa: ZamówienieKlienta
- atrybut: numerZamówienia;
- operacja: dodajProduktDoZamówienia().
Specyfikując atrybuty i operacje możemy również podać ich typy. W przypadku atrybutów są to typy danych jakie atrybuty te przechowują. W przypadku operacji oznacza to typ danych jaki operacja zwraca po zakończeniu swojego działania. Typy atrybutów i operacji umieszczane są po samej nazwie i poprzedzane dwukropkiem, np. nrZamówienia : Integer. Parametry operacji także są opisywane typami danych. Standardowymi typami danych są typy stosowane przez większość popularnych języków programowania. Są to np. Boolean, Integer, Double, Long, Decimal, String, Char, itp. Poza tym atrybutem może być obiekt innej klasy. Wtedy typem danych jest klasa, której instancją jest przechowywany obiekt.
Opcjonalnie w przypadku atrybutów mogą być stosowane liczebności i ograniczenia. Liczebność atrybutu jest podawana w nawiasach kwadratowych po typie danych (np. suma : Integer [0..*]). Ograniczenia mogą opisywać domyślną wartość atrybutu (np. skladka : Double [1..5] = 100) lub jego właściwość ze słowem kluczowym w nawiasie klamrowym. Poniżej kilka przykładów właściwości:
- {ordered} – uporządkowany;
- {unordered} – nieuporządkowany;
- {unique} – nie powtarza się;
- {readOnly} – tylko do odczytu.
- *{list} – dowolna liczba kolejnych, mogących się powtarzać elementów;
- 1..*{ordered} – jeden lub więcej kolejnych unikatowych elementów;
- 1..6{bag} – losowe powtarzające się elementy w zakresie od 1 do 6.
Klasy, które nie mogą mieć swoich instancji nazywane są klasami abstrakcyjnymi. Klasa abstrakcyjna pełni rolę uogólnienia (generalizacji) klas będących na niższych poziomach w hierarchii dziedziczenia. Celem stosowania klasy abstrakcyjnej jest stworzenie zestawu atrybutów i operacji służących tylko do dziedziczenia. Klasa abstrakcyjna różni się od interfejsu tym, że może posiadać implementację swoich operacji, a interfejs nie. Nazwy klas abstrakcyjnych pisane są zazwyczaj kursywą lub stosowany jest przy nazwie klasy stereotyp <<abstract>>.
Interfejs
Interfejs jest zestawem operacji stanowiących usługi oferowane przez klasę lub komponent. Zawiera deklaracje operacji bez specyfikacji implementacji. Ma to na celu zidentyfikowanie wspólnych zachowań różnych klas, które są realizowane w różny sposób. Interfejs oznacza się przez użycie stereotypu <<interface>>. Interfejsy nie mogą posiadać instancji. Możliwe jest tylko jego implementowanie, czyli nadpisywanie operacji interfejsu przez inne klasy. Jeden interfejs może być implementowany przez wiele klas. Tym samym jedna klasa może implementować wiele interfejsów. Notacyjnie jest to rozwiązanie podobnie jak w przypadku dziedziczenia, tylko że w tym przypadku linia strzałki skierowanej od klasy implementującej do interfejsu jest przerywana. UML 2.0 wprowadza dodatkowo dwa rodzaje interfejsów zwanych łącznikami. Pierwszy z nich jest interfejsem dostarczającym (provided interface) i wskazuje, że może dostarczyć interfejs innym klasom. Drugi jest interfejsem wymaganym (required) i oznacza interfejs wymagany do funkcjonowania obiektów klasy. W obu tych przypadkach używana jest notacja gniazd (ball-socket notation). Notacja ta ukrywa szczegółową budowę interfejsu. Podkreśla natomiast informację na temat udostępnianych i wymaganych przez daną klasę interfejsów.
Związki
Klasy na
diagramie klas połączone są związkami. Powszechnie stosowane są cztery rodzaje
związków:
- asocjacje,
- generalizacje,
- zależności,
- realizacje.
Asocjacja opisuje powiązania między
obiektami klas. Wyróżnia się asocjacje binarce i n-arne.
Najczęściej stosuje się asocjacje binarne, które łączą bezpośrednio dwie klasy.
Asocjacje n-arne łaczą więcej niż dwie klasy. Każda asocjacja może być
dokładniej opisana przez zestaw cech, tj. nazwę, role powiązanych klas,
nawigację, liczebność, agregację. Nazwa asocjacji odzwierciedla istotę związku. Przy nazwie
asocjacji można opcjonalnie dodać kierunek jej odczytu. Nazwa asocjacji może
być także scharakteryzowana poprzez role klas uczestniczących w asocjacji.
W związku
binarnym, rola jest powinnością pełnioną przez jedną klasę wobec drugiej klasy.
W związku n-arnym role można przypisać każdej z powiązanych klas. Rola
spełniana przez daną klasę jest umieszczania bezpośrednio przy tej klasie.
Każda
asocjacja domyślnie jest dwukierunkowa. To znaczy, że komunikacja między
obiektami połączonych klas jest dwustronna. W przypadkach, gdy istnieje
potrzeba wskazania kierunku asocjacji, stosuje się nawigację jednokierunkową.
Zastosowanie asocjacji dwukierunkowej nie wyklucza komunikowania się klas w obu
kierunkach – może to być zrealizowane przez asocjacje z innymi klasami.
UML 2.0
dopuszcza stosowanie wyłączenia nawigacji (navigation
exclusion). Chodzi tutaj o jawne zablokowanie przepływu komunikacji w
określoną stronę. Przedstawia się to za pomocą krzyżyka po stronie końca asocjacji, w
którego kierunku domyślnie przebiegałaby nawigacja. Dopuszczalna jest także
(chociaż rzadko stosowana) asocjacja ze strzałkami w obu kierunkach. Oznacza
ona, że komunikacja na pewno przebiega w obie strony. Ten sposób prezentowania
asocjacji jest tożsamy ze standardową formą asocjacji dwukierunkowej (bez
strzałek na obu końcach).
Dopuszczalna
liczba obiektów biorących udział w związku jest określona przez liczebność. Liczebność
określa maksymalną liczbę obiektów jednej klasy, które mogą być w związku z
jednym obiektem drugiej klasy. Liczebność lokuje się przy końcach asocjacji.
Rozważmy asocjację pomiędzy klasami A i B. Liczebność x umieszczona przy końcu
asocjacji bliżej klasy B oznacza, że na jeden obiekt klasy A może przypadać do
x (lub zakres wyrażony przez x) obiektów klasy B. Poniżej możliwe oznaczenia
liczebności asocjacji:
- 1 – dokładnie jeden;
- 1..* - od jednego do wielu;
- 0..1 – od zera do jednego;
- * - wiele (od zera do nieskończoności);
- 0..* - jak wyżej;
- n – dokładnie n (n>1), np. 21;
- 1..n – od jednego do n (n>1), np. 1..8;
- 0..n – od zera do n (n>0), np. 0..5;
- n..m – od n do m (m>n>1), np. 4..13;
- n..* - od n do nieskończoności (n>1), np. 3..*;
- n,m,o..p,q – liczebność złożona, np. 1,4,7..9,12
Związek
między klasami może być dokładniej opisany za pomocą klasy asocjacyjnej. Jest
to klasa, która dokumentuje związek między klasami, wskazuje na powiązane ze
sobą obiekty i charakteryzuje ten związek poprzez swoje atrybuty i operacje. Klasa
asocjacyjna ma dostęp do obiektów uczestniczących w relacji. Nie przechowuje
informacji związanych z klasami uczestniczącymi w asocjacji. Każda asocjacja
może mieć przypisaną maksymalnie jedną instancję klasy asocjacyjnej. Tak więc,
klasa asocjacyjna jest pewnego rodzaju interpretacją asocjacji między klasami.
Stosowanie tego rodzaju klas znacząco ułatwia zapanowanie nad asocjacjami
n-arnymi. Graficznie element ten jest prezentowany jako klasa połączona linią
przerywaną z asocjacją.
Kiedy powiązane ze sobą klasy mogą pełnić wobec siebie kilka
różnych ról, mamy do czynienia z asocjacją wielokrotną. Liczba asocjacji między
klasami zależy wtedy od liczby ról. Tak więc, dwie klasy mogą być połączone ze
sobą wieloma asocjacjami, które charakteryzują różne sposoby interpretacji
relacji między nimi.
W przypadku,
gdy obiekty tej samej klasy pełnią wobec siebie jakieś role, stosuje się
asocjację zwrotną. Asocjacja zwrotna wiążę klasę z samą sobą.
Kolejną
cechą asocjacji jest kwalifikacja, która umożliwia wyszukiwanie obiektów
związanych z daną asocjacją. Kwalifikacji dokonuje się za pomocą kwalifikatora,
czyli atrybutu lub listy atrybutów, których wartości porządkują zbiór obiektów
tej klasy występujących w danej asocjacji. Innymi słowy, kwalifikacja pozwala
wskazać, który atrybut jednej z klas służy do zapewnienia unikatowego związku.
Atrybut ten jest kwalifikatorem tego związku.
Szczególnym
rodzajem asocjacji jest agregacja. Agregacja odzwierciedla związek typu „całość
– część”. Istnieją dwa rodzaje agregacji:
- agregację częściową (nazywaną po prostu agregacją) i
- agregację całkowitą – kompozycję.
Agregacja całkowita jest oznaczana wypełnionym rombem,
agregacja częściowa – pustym. W agregacji uczestniczą dwa elementy – agregat,
czyli obiekt stanowiący „całość” i obiekt składowy, będący „częścią” całości. Obiekt
klasy reprezentującej „całość” posiada jako swoje komponenty obiekty „części”.
Wartościami atrybutów obiektu zagregowanego mogą być obiekty będące w relacji
jako „części”.
Przy
agregacji całkowitej obiekty częściowe nie mogą funkcjonować samodzielnie i
niezależnie. Usunięcie agregatu powoduje automatyczne usunięcie jego części.
Ponadto, obiekty składowe mogą być kreowane tylko po utworzeniu elementu
agregującego. W agregacji częściowej usunięcie agregatu nie skutkuje usunięciem
jego części. W tym przypadku obiekty współdzielone mogą funkcjonować
samodzielnie, niezależnie od agregatu.
Kolejnym związkiem stosowanym na diagramie klas jest związek
generalizacji. Generalizacja łączy element ogólny – klasę nadrzędną – z
elementem bardziej szczegółowym – klasą podrzędną. Klasa podrzędna jest zgodna
z klasą nadrzędną, tzn. posiada te same atrybuty i operacje. Mówi się wtedy o
dziedziczeniu właściwości strukturalnych i behawioralnych. Ponadto, klasa
podrzędna może nadpisywać te właściwości, a także definiować nowe. Obiekt
wywodzący się z klasy podrzędnej może być użyty wszędzie tam, gdzie obiekt
klasy nadrzędnej. Obiekt klasy podrzędnej zawsze może zastąpić obiekt klasy
nadrzędnej. Nie działa to jednak w drugą stronę. W przypadku, gdy klasa ma tylko
jedną klasę nadrzędną mówi się o dziedziczeniu pojedynczym. W przypadku wielu
klas nadrzędnych jest to dziedziczenie wielokrotne. Związek generalizacji
modeluje się za pomocą strzałki zakończonej zamkniętym, pustym grotem o
kierunku od klasy podrzędnej do klasy nadrzędnej.
Zależność
definiuje związek dwóch elementów, w którym zmiana zachodząca w jednym z nich
(niezależnym – źródłowym) pociąga za sobą zmianę w drugim (zależnym – docelowym).
Typ związku zależności może być określony stereotypem, takim jak np.:
- <<call>> - źródło wywołuje cel;
- <<create>> - źródło tworzy obiekt docelowy;
- <<deriver>> - źródło można wyznaczyć na podstawie celu;
- <<import>> - zawartość publiczna celu zostaje dołączona do obszaru nazw źródła;
- <<instance>> - źródło tworzy egzemplarz klasy;
- <<permit>> - cel pozwala źródłu na dostęp do swoich cech prywatnych;
- <<realize>> - źródło jest implementacją specyfikacji lub interfejsem definiowanym przez cel;
- <<refine>> - źródło jest na doskonalszym poziomie abstrakcji niż cel;
- <<substitute>> - źródło jest substytutem celu;
- <<trace>> - cel jest historycznie przodkiem źródła;
- <<use>> - źródło wymaga celu do swojego funkcjonowania.
Klasy na
diagramie klas mogą brać również udział w związkach realizacji – jedna strona
związku wskazuje klasę określającą kontrakt, a druga klasę zapewniającą
wywiązanie się z niego. Realizacja często wskazuje na związek pomiędzy klasą a
interfejsem – interfejs zapewnia realizację usługi klasy. Na diagramie
realizacja jest oznaczona jako strzałka z linią przerywaną zakończoną
zamkniętym, pustym grotem, skierowana w kierunku od „kontraktu” do jego
realizacji. Przy związku dodawany jest także stereotyp <<realize>>.
W przypadku notacji gniazdowej (socket-ball) „kontrakt” jest powiązany linią
ciągłą z interfejsem dostarczającym.
Brak komentarzy:
Prześlij komentarz