Pobierz
Transkrypt
Pobierz
Podstawowe własności języków obiektowych Definicja i znaczenie klas Dualna natura klasy • moduł architektury systemu informatycznego Pracownik # imię # nazwisko # etat - płaca + zatrudnij() + awansuj(Etat) + dajZasiłek(float) + przydzielZadanie() class Pracownik ( float płaca; protected: char *imię; char *nazwisko; char *etat; public: void zatrudnij(); … }; w przeciwieństwie do klasycznej dekompozycji funkcjonalnej void zatrudnij() Kadry Dział Socjalny { …} void przenieś() zatrudnij() dajZasiłek() { …} przenieś() dajPożyczkę() void awansuj(char*) { …} awansuj(Etat) … zwolnij() • typ danych użytkownika (abstrakcyjny typ danych) Complex c1(1.4, 3.2), c2(3.9, 3.2), c3; c3 = c1 + c2; c3 = c1.max(c2); Architektura programów proceduralny paradygmat programowania Problem jest modelowany jako zbiór algorytmów i implementowany jako zbiór funkcji: struct TablicaLiczb { unsigned int rozmiar; float *fp; }; #include "TablicaLiczb.h" void Podstaw(TablicaLiczb &tab, float el, unsigned ind) { if ind>=0 && ind<tab.rozmiar tab.fp[ind] = el; else BłądZakresu(ind); } #include "TablicaLiczb.h" float Pobierz(TablicaLiczba &tab, unsigned int ind) { if ind>=0 && ind<tab.rozmiar return tab.fp[ind]; else BłądZakresu(ind); } #include "TablicaLiczb.h" void funkcja () { TablicaLiczb tl; tl.rozmiar = 100; tl.fp = new float[100]; Podstaw(tl, 7.58, 0); float f = Pobierz(tl, 0); } moduł A moduł B moduł C moduł D Kolor czerwony pokazuje zależności implementacyjne między modułami. Zależności dotyczą struktur i typów danych. Obiektowy paradygmat programowania Problem jest modelowany i implementowany jako zbiór klas class TablicaLiczb { moduł A protected: unsigned int rozmiar; float *fp; public: TablicaLiczb (unsigned int); float &operator[] (unsigned); unsigned max() return rozmiar; ~TablicaLiczb (); }; TablicaLiczb::TablicaLiczb (unsigned int r) { fp = new float[rozmiar=r]; } float &TablicaLiczb::operator[] (unsigned ind) { if (ind>=0 && ind< rozmiar) return *(fp+ind); else throw BłądZakresu(ind); } TablicaLiczb::~TablicaLiczb ( ) { delete [] fp; } ... TablicaLiczb tl(100); tl[0] = 7.58f; float f = tl[0]; ... moduł B Zależności dotyczą tylko specyfikacji, a nie implementacji. Twórca modułu A zachował niezależność w wyborze struktur i typów danych. Definicja klasy w języku C++ Definicja klasy umożliwia wyspecyfikowanie części strukturalnej – dane i behawioralnej – metody. TablicaLiczb # rozmiar : Integer # elementy [1..*] : Float << konstruktor >> + Utwórz (Integer) << destruktor >> + Usuń () << metody dostępu >> + Wstaw (Integer, Float) + Pobierz (Integer) : Float class TablicaLiczb { protected: // atrybuty obiektów unsigned int rozmiar; float *fp; public: // metody TablicaLiczb (unsigned int); // konstruktor // dostęp do elementów tablicy void wstaw(unsigned, float); float pobierz(unsigned) const; ~TablicaLiczb (); // destruktor }; Powyższa definicja klasy nie obejmuje implementacji metod. W języku C++ klasa jest pomocniczą jednostką architektury. Podstawową jednostką pozostają funkcje. Konstruktory i destruktory obiektów Konstruktory i destruktory obiektów są wyspecjalizowanymi metodami klasy służącymi do tworzenia i usuwania obiektów. Konstruktory umożliwiają inicjowanie atrybutów obiektów w momencie ich tworzenia. Klasa może mieć kilka konstruktorów. Wszystkie one muszą mieć tę samą nazwę, taką samą jak nazwa klasy i muszą różnić się listą typów parametrów wejściowych. Konstruktory muszą być beztypowe. Formalnie konstruktory są metodami klasy. Brak jawnie zdefiniowanego konstruktora oznacza, że klasa posiada jeden systemowy i bezparametrowy konstruktor. TablicaLiczb (unsigned int); TablicaLiczb (const TablicaLiczb&); Destruktory służą do zwalniania zasobów: pamięci, plików, itp. Klasa może mieć dokładnie jeden destruktor. Nazwą destruktora musi być nazwa klasy poprzedzona znakiem tyldy "~". Destruktor musi być metodą beztypową i bezparametrową. Destruktor jest metodą uruchamianą w momencie usuwania obiektów. ~TablicaLiczb (); Cykl życia obiektów • Wywołanie jednego z konstruktorów obiektu, w szczególności domyślnego, w celu utworzenia obiektu, inicjacji zmiennych obiektu i przydzielenia zasobów do obiektu: przydział pamięci operacyjnej, otwarcie plików, utworzenie wątków, nawiązanie połączeń, otwarcie sesji, itp. • Korzystanie z obiektu za pomocą metod tworzących jego interfejs i potencjalnie zmieniających stan obiektu. Kod metod powinien spełniać tak zwane niezmienniki klas, definiujące poprawne stany obiektu. • Wywołanie - jawne lub niejawne – destruktora obiektu, w celu zapewnienia spójnego stanu systemu poprzez zwolnienie przydzielonych zasobów. operacje klasa konstruktor obiekt destruktor Przesyłanie komunikatów Metody są funkcjami nierozerwalnie związanymi z obiektami, które są wystąpieniami klasy. Kontekstem ich działania są obiekt, na rzecz których są wywoływane. Obiekt tworzony automatycznie: komunikat zmienna . nazwa_metody(parametry); operator przesłania komunikatu obiekt ‐ odbiorca komunikatu Obiekt tworzony dynamicznie: zmienna -> nazwa_metody(parametry); operator przesłania komunikatu // alternatywne wywołania konstruktorów TablicaLiczb tl1(100); TablicaLiczb tl2 = TablicaLiczb(tl1); TablicaLiczb *tl3p = new TablicaLiczb(12); // przesyłanie komunikatów do obiektów tl1.wstaw(0, 7.58f); tl1.wstaw(1, 3.11f); tl2.wstaw(0, tl1.weź(1)); tl2.wstaw(1, 13.01f); tl3->wstaw(0, -0.001f); Mechanizm przesyłania float x = tl1.weź(0); komunikatów jest w języ// wywołanie destruktora ku C++ uzupełnieniem delete tl3; mechanizmu wywoływa- nia funkcji. Przeciążanie operatorów Dla upodobnienia klas do predefiniowanych typów danych niektóre języki obiektowe umożliwiają wykorzystanie standardowych operatorów jako nazw metod. Mechanizm ten jest nazywany przeciążaniem operatorów. Mechanizm ten polega na nadaniu standardowym operatorom nowej semantyki (implementacji), przy zachowaniu składni tych operatorów. W języku C++ składnia dla definiowania operatorów przeciążonych jest następująca: Słowo kluczowe: operator skonakatenowane z symbolem przeciążanego operatora. float &operator[] (unsigned indeks) { if ind>=0 && ind< rozmiar return *(fp+ind); else throw BłądZakresu(ind); } Complex operator+= (Complex c2) { Zmienna this jest typu: this->re += c2.re; wskaźnik na daną klasę. this->im += c2.im; Jej wartością jest adres return *this; bieżącego obiektu. } Implementacja metod W składni języka C++ przewidziano możliwość definiowania metod jako funkcji typu in-line, których kod wykonywalny jest rozwijany we wszystkich miejscach ich wywoływania. Implementacja metod in-line jest częścią definicji klasy. Zalecanym sposobem implementacji metod jest implementacja poza ciałem definicji klasy. class TablicaLiczb { protected: unsigned int rozmiar; float *fp; public: TablicaLiczb (unsigned int); // metoda in‐line unsigned max() {return rozmiar;} // implementacja // "zwykła" metoda float &operator[] (unsigned); ~TablicaLiczb (); }; TablicaLiczb::TablicaLiczb (unsigned int r) { fp = new float[rozmiar=r]; } // implementacja metody float &TablicaLiczb::operator[] (unsigned ind) { operator zasięgu if (ind>=0 && ind< rozmiar) return *(fp+ind); else BłądZakresu(ind); } TablicaLiczb::~TablicaLiczb ( ) { delete [] fp; } DefinicjaklasywjęzykuJava TablicaLiczb # rozmiar : Integer # elementy [1..*] : Float << konstruktor >> + Utwórz (Integer) << destruktor >> + Usuń () << metody dostępu >> + Wstaw (Integer, Float) + Pobierz (Integer) : Float package Tablica; // definicja pakietu class TablicaLiczb { // atrybuty protected Float magazyn [ ]; protected int rozmiar; // definicja i implementacja metod // konstruktory public TablicaLiczb (int rozmiar) { magazyn = new Float [this.rozmiar = rozmiar]; } // metody dostępu – brak przeciążonych operatorów public wstaw (float element, int ind) { magazyn[ind] = element; } public Float wez (int ind) { return magazyn[ind]; } // metoda finalize() - w tym wypadku niepotrzebna } Klasyw wjęzy ykuJJava a programów pisanyc ch w ję ęzyku J ava jes st zaArchitektura ą kolekc cją pakkietów, które składają ą się z komgnieżżdżoną pletn nych de efinicji klas. C Cały wykonyw w walny kkod pro ogramów w znajdu uje się w meto odach klas. k Pakiet 1 class A class B P Pakiet 2 class K class L a może e posia adać kilka bez ztypowych ko onstrukttorów Klasa rozró óżnialnyych za pomoccą listy y typów w param metrów wejściow wych. W języku Ja ava nie e ma kla asyczn nych de estrukto orów. ObiekO e są niejawn n ie. Mo ożliwe jest j za a to rę ęczne ty ussuwane zwalnianie zasobó ów, inn nych niż ż pamięć ope eracyjna a, za pomo ocą wyróżnio onej m metody finalize e(), w mome encie usuw wania obiektu o przez p proces odświe ecania p pamięc ci. Języyk Java a nie udostęp u pnia mechani m izmu p przeciąż żania prede efiniow wanych operato orów. Hermetyczność klas Część publiczna Część ukryta klasy Hermetyczność klas ukrywa przed ich użytkownikami wybrane cechy klas. Dzięki temu nawet jeśli klient klasy zna jego wewnętrzną implementację, to nie może tej wiedzy wykorzystać podczas korzystania z niej. Dzięki temu zmiany odnoszące się do implementacji, a nie funkcjonalności klasy nie są propagowane między klasami. Ograniczenie interfejsów klas do ich własności funkcjonalnych zwiększa autonomię i uniwersalność klas. Stosowanie hermetyczności Projekt każdej klasy powinien rozróżniać cechy implementacyjne, które będą ukryte przed innymi modułami systemu od cech funkcjonalnych, które mogą być udostępniane innym modułom. Część publiczna: • specyfikacja funkcjonalności Część ukryta: • reprezentacja • implementacja funkcji Poprawne rozwiązanie polega na upublicznieniu funkcjonalności klasy (co klasa robi) i ukryciu implementacji (jak to robi). Hermetyczność Nowe rodzaje zasięgu zmiennych i funkcji. Zasięg elementów klasy jest jawnie specyfikowany za pomocą jednego z trzech kwalifikatorów zasięgu: • private – dostęp do zmiennych (atrybutów obiektu) i metod o zasięgu private mają metody wszystkich wystąpień danej klasy; jest to zasięg domyślny; • protected – dostęp do zmiennych i metod o zasięgu protected mają metody wszystkich wystąpień danej klasy i klas potomnych; • public – dostęp do zmiennych i metod o zasięgu public mają metody wszystkich klas oraz wszystkie funkcje programu. Twórca klasy: private Twórcy klas pochodnych: protected Użytkownicy obiektów: public Operatory zasięgu w języku C++ class TablicaLiczb { private: unsigned int rozmiar; float *fp; protected: void resize(unsigned int); public: TablicaLiczb (unsigned int); TablicaLiczb (const TablicaLiczb&); float &operator[] (unsigned); }; // twórca klasy TablicaLiczb::TablicaLiczb ( const TablicaLiczb &t) { rozmiar = t.rozmiar; // dostęp do elementów // prywatnych innych obiektów tej samej klasy fp = new float[rozmiar]; } // twórca klasy pochodnej TablicaRozszerzalna::operator=(const TablicaLiczb &t) { if (rozmiar != t.rozmiar) // błąd - niedostępne resize(r); // OK – protected ... // dostęp publiczny TablicaLiczb t(12); TablicaRozszerzalna t.rozmiar=18; tr.resize(15); t[0]=512; tr[1]=1; tr(10); // błąd – atrybut niedostępny // błąd – metoda niedostępna // OK – metoda publiczna // OK – metoda publiczna W językach Java i C++ zasięg prywatny nie rozdziela obiektów, które są wystąpieniami tej samej klasy. Metodyka stosowania hermetyczności Zamiast: class Punkt { public: float współrzędnaX, współrzędnaY; }; Implementacja klasy Odcinek jest class Odcinek { public: zależna od implementacji klasy Punkt Punkt p, k; Odcinek &Przesuń(float, float); }; Odcinek &Odcinek::Przesuń(float x, float y) { p.współrzędnaX += x; p.współrzędnaY += y; k.współrzędnaX += x; k.współrzędnaY += y; } Powinno być: class Punkt { protected: float współrzędnaX, współrzędnaY; public: Punkt &Przesuń(float, float); ... }; Implementacja klasy Odcinek jest ... niezależna od implementacji klasy Punkt Odcinek &Odcinek::Przesuń(float x, float y) { p.przesuń(x, y); k.przesuń(x, y); } Jedynym uzasadnieniem dla pozostawienia zależności mię‐ dzy implementacjami klas jest występowanie kluczowych problemów wydajności. Korzyści z hermetyzowania implementacji klas class Punkt { protected: float współrzędnaX, współrzędnaY; public: Punkt (float, float); Punkt &Przesuń (float, float); Punkt &Obróć (float); ... }; Punkt::Punkt(float x, float y) { współrzędnaX = x; współrzędnaY = y;} ... void main() { Kod w funkcji main() nie Punkt p(12.8, -0.9); p.przesuń(10.0, 5.5); wymaga zmian. ... } Radykalna modyfikacja implementacji klasy Punkt niezmieniająca specyfikacji interfejsu: ... protected: float kąt, moduł; ... Punkt::Punkt(float x, float y) { moduł = sqrt(x*x+y*y); kąt = arccos(x/moduł); } nie wymaga zmian w implementacji modułów korzystających z usług tej klasy. Przyjaciele klasy Uzupełnieniem funkcjonalności podstawowego mechanizmu ukrywania informacji w C++ są przyjaciele klasy (ang. friend). Przyjacielem klasy może być funkcja, wybrana metoda innej klasy, lub wszystkie metody innej klasy. Zaprzyjaźniona funkcja lub metoda ma dostęp do prywatnych i chronionych elementów innej klasy. Przyjaźń nie jest relacją zwrotną ani przechodnią oraz nie podlega dziedziczeniu. int f(float); class A { ... void fa( ); }; class B { ... void fb1( ); void fb2( ); }; C - ac : Integer class C { ... - fc() private: int ac; void fc( ); ... friend int f(float); friend void A::fa(float); friend class B; }; <<friend>> A + fa() void A::fa( ) { C c; c.ac = 0; /* OK Metoda fa jako przyjaciel widzi prywatne wnętrze obiektu c */ ... } Hermetyczność w języku Java Elementy składowe programów • obiekty • klasy • pakiety Dostępność elementów składowych obiektów: • • • • private protected public friendly (domyślny, niejawny) Zasięg object class subclass package world private 9 9 protected 9 9 9 9 public 9 9 9 9 friendly 9 9 9 Dostępność klas: • public • private (domyślny, niejawny) Zasięg package private 9 public 9 world 9 9 Definicja klasy w języku SmallTalk Array Subclass: #FixedSizeCollection instanceVariableNames: 'base' classVariableNames: '' poolDictionaries: '' category: nil new: size ^self new: size base: 1 at: i self rangeCheck: i. ^self basicAt: (i-base) at: i put: v self rangeCheck: i. ^self basicAt: (i-base) put: v ... |tablica element| tablica := Array new: 10. tablica at: 1 put: 'Ala ma kota'. tablica at: 2 put: 13. element := tablica at: 1 W języku SmallTalk operatory zasięgu nie są dostępne. Z definicji wszystkie zmienne są prywatne, a wszystkie metody są publiczne. Hermetyczność dotyczy pojedynczych obiektów. Brak środków składniowych do odwołania się do elementów prywatnych obiektu z jego zewnętrza. SmallTalk-80 Wszystko jest obiektem • obiekty dziedziny wdrożenia: klient, zamówienie, towar • interfejs: przycisk, okno, edytor tekstu • podstawowe typy danych: string, set, numbers, booleans • środowisko programistyczne: browser, debugger, compiler • elementy języka: klasa, metoda, struktury kontrolne Własności • Przetwarzanie jest realizowane tylko i wyłącznie za pomocą metod – brak predefiniowanych operatorów. • Wszystkie metody są publiczne, a atrybuty prywatne. Brak składni dla odwołania się do atrybutów innego obiektu. Hermetyczność dotyczy pojedynczych obiektów • Zmienne są nietypowane. Abstrakcje Abstrakcja – uproszczenie problemu umożliwiające skoncentrowanie na jego podstawowych elementach Klasyczna abstrakcja funkcjonalna Ładowanie systemu Wczytaj znak Wyświetlenie znaku zachęty Edycja polecenia inne Enter Backspace Usuń znak Analiza polecenia … Wyświetl znak Abstrakcje obiektowe Kompozycja – abstrahowanie od szczegółów struktury wewnętrznej klasy Pojazd # typ # nadwozie # silnik + jedź() Pojazd # typ # nadwozie + jedź() Silnik # typ zawiera # pojemność 1 1 # zawory + zapal() Generalizacja – abstrahowanie od pewnych własności klasy Pojazd # typ # nadwozie # silnik + jedź() Samochód # koła [4] + jedź() Implementacja związków Obiekty świata rzeczywistego są powiązane różnymi semantycznymi związkami. Związki te mają różną semantykę, dotyczącą na przykład siły powiązań: • Powiązanie • Agregacja • Kompozycja Związki te muszą być odwzorowane za pomocą pojęć języków obiektowych. • Powiązania – wskaźniki i referencje na obiekty powiązane. • Agregacje i kompozycje – obiekty złożone. Związek kompozycji C++ Naturalną implementacją związku kompozycji jest rozwijanie obiektu składowego w ciele obiektu złożonego. Życie takiego obiektu jest zależne od czasu życia obiektu go zawierającego. Współdzielenie obiektu składowego jest trudne. Samochód zawiera 1 1 Silnik class Silnik { public: Silnik(int pojemność); ... }; class Samochód { protected: ... Silnik silnik; public: Samochód(char* typ, int pojemność); ... }; Obiekty składowe muszą być inicjowane przed inicjacją zawierających je obiektów złożonych. Wymaga to dodatkowej składni dla przekazania parametrów wejściowych dla konstruktorów. // inicjacja obiektu składowego Samochód::Samochód(char *typ, int pojemność): silnik(pojemność) // wywołanie konstruktora { ... } Związek kompozycji C++ Alternatywną implementacją związku kompozycji jest powiązanie obiektów za poprzez zmienne wskaźnikowe. class Silnik { ... }; class Samochód { protected: ... Silnik *silnik; public: Samochód(char*, int); ... }; Obiekty składowe powinny być inicjowane w konstruktorze obiektów złożonych. // inicjacja obiektu składowego Samochód::Samochód(char *typ, int pojemność){ // wywołanie konstruktora obiektu składowego silnik = new Silnik(pojemność); ... } Samochód::~Samochód(){ // wywołanie destruktora obiektu składowego delete silnik; ... } Związek kompozycji Java W języku Java wszystkie obiekty są przypisane do zmiennych referencyjnych, to znaczy, że ich ciało nie jest rozwijane w ciele obiektu złożonego. Dla zapewnienia zależności i wyłączności, obiekty składowe powinny być definiowane jako prywatne i inicjowane w konstruktorze obiektów złożonych. class Silnik { ... }; class Samochód { private String model; private Silnik silnik; public Samochód(String mod, int poj) { model = mod; silnik = new Silnik(pojemność); ... } ... }; Prywatny obiekt składowy przypisany zmiennej silnik po usunięciu obiektu złożonego jako nieosiągalny, będzie usunięty przez systemowe procesy odśmiecania pamięci. Kopiowanie powiązań Można wyróżnić dwa sposoby tworzenia kopii obiektów złożonych za pomocą związków kompozycji implementowanych za pomocą wskaźników lub referencji: kopia płytka i kopia głęboka. Dla obiektów powiązanych: o1’ o1 o11 o12 Kopia płytka o121 Dla obiektów złożonych: o1’ o1 Kopia głęboka o11 o12 o121 o11’ o12’ o121’ Płytka kopia obiektów powiązanych związkiem kompozycji narusza semantykę wyłączności !!! (Java, referencje i wskaźniki w C++) Kopiowanie obiektów złożonych w C++ Operator podstawienia realizuje operację płytkiego kopiowania obiektów złożonych powiązanych referencjami z obiektami składowymi. class Punkt { private: float w_x, w_y; public: Punkt(float, float); void Przesuń(float, float); }; class Odcinek { private: Punkt *w1; Punkt *w2; public: Odcinek(Punkt, Punkt); void Przesuń(float, float); }; ... operator Odcinek o1, o2; kopiowania ... o1 = o2; // płytka kopia obiektu ... o1 w1 o2 w2 Przeciążenie operatorakopiowaniaC++ Mechanizm przeciążania operatorów umożliwia zmianę semantyki operatora kopiowania "=". class Odcinek { private: Punkt *w1; Punkt *w2; public: Odcinek(Punkt, Punkt); void Przesuń(float, float); Odcinek& operator=(const Odcinek &); }; Odcinek &Odcinek::operator=(const Odcinek &o) { if (w1 || w2) { delete w1; delete w2; } w1 = new Punkt(o->w1); w2 = new Punkt(o->w2); return *this; } Odcinek o1, o2; ... o1 = o2; // głęboka kopia obiektu ... o1 w1 o2 w2 w1 w2 Niejawnekopiowanie obiektówzłożonychwC++ Cechą charakterystyczną języka C++ jest przekazywanie parametrów i wartości zwrotnej funkcji i metod przez kopiowanie wartości. Dla obiektów złożonych wykonywane są to płytkie kopie. void funkcja(TablicaLiczb x, TablicaLiczb y) { ... } Obiekty x i y zostaną utworzone jako płytkie kopie aktualnych parametrów wywołania funkcji. Wykonanie głębokiej kopii obiektów wymaga definicji dodatkowego konstruktora o nagłówku X(const X&), gdzie X jest nazwą klasy. class TablicaLiczb { ... public: TablicaLiczb (unsigned); TablicaLiczb (const TablicaLiczb&); ... }; TablicaLiczb::TablicaLiczb(const TablicaLiczb& t){ fp = new float[rozmiar=t.rozmiar]; for( int i=0; i<rozmiar; i++) fp[i]=t.fp[0]; } Głębokakopiaobiektów wjęzykuJava Do płytkiego kopiowania w języku Java służy systemowa metoda clone(). Metoda ta dokonuje podstawienia obiektów składowych oryginału pod zmienne referencyjne kopii obiektu złożonego. Odcinek o1, o2; o1 = New Odcinek(3.45, 1.12); o2 = o1.clone(); Tworzenie głębokich kopii obiektów wybranej klasy wymaga redefiniowania metody clone() w danej klasie lub zdefiniowania własnej metody kopiowania. Odcinek o1, o2, o3; o1 = New Odcinek(3.45, 1.12); o2 = o1.clone(); o3 = o1.deepcopy(); ... Rozróżnienie głębokich i płytkich kopii wymaga rozróżnienia tożsamości i równości obiektów. // weryfikacja tożsamości dwóch obiektów if (o1.Wierzchołek1() == o2.Wierzchołek2()) ... // weryfikacja równości dwóch obiektów if (o1.Wierzchołek1().equal(o2.Wierzchołek2())) ... Metrykiprogramówobiektowych Metryki LCOM (Lack Cohesion of Methods) LCOM1 Dwie zmienne P (niespoistość) i Q (spoistość) są wyznaczane następująco: dla każdej pary metod w klasie, jeżeli wykonują one dostęp do rozłącznych zbiorów zmiennych klasy zwiększ P o jeden, jeżeli współdzielą choć jedną zmienną zwiększ Q o jeden. LCOM1 = P – Q, jeżeli P > Q LCOM1 = 0, w przeciwnym wypadku. LCOM2, LCOM3 m – liczba metod klasy a – liczba zmiennych klasy mAi – liczba metod, które wykonują dostęp do zmiennej Ai sum(mAi) – suma mAi dla wszystkich zmiennych klasy LCOM2 = 1 - sum(mAi)/(m*a) LCOM3 = (m - sum(mAi)/a) / (m-1) LCOM4 Dwie metody klasy są powiązane jeżeli: • wykonują dostęp do tej samej zmiennej obiektu, • jedna z nich wywołuje drugą. LCOM4 jest równy liczbie rozłącznych grup metod Metry M yki LCOM L M Metrryka LC COM4 Klasa aA Klasa aB LCO OM1(A) P=9, Q=1 Q L LCOM1 1(A) = 8 LCOM1(B) P=8, P Q=2 LCOM1 L 1(B) = 6 LCO OM2(A),, LCOM M3(A) m=5 a=2 m mx = 1 m my = 2 ssum(mA) = 3 2(A) = 0,7 0 LCOM2 L LCOM3 3(A) = 0,875 0 LCOM2(B) , LCOM M3(B) m=5 a=2 mx m =2 my m =2 sum(mA s A) = 4 LCOM2 L 2(B) = 0 0,6 LCOM3 L 3(B) = 0 0,75 LCO OM4(A) LCOM4 4(A) = 2 LCOM4(B) LCOM4 L 4(B) = 1 Mettrykii TCC C i LCC L ś ścisł ej i luźne ej ko ohezjji N – licczba metod m w klasiie NP = N*(N-1 1)/2 – maksyymalna a liczba a połącczeń ba bezp pośred dnich powiąz p ań w g grafie metod m NDC – liczb NID – liczba a pośre ednich powią ązań Meto ody a i b są be ezpośre ednio po owiązane jeże eli: 1. o obydwiie przettwarzają tę sa amą zmienną w wystąpienia 2. łłańcuch hy wyw wołań ro ozpocz zynając ce się w a i b dod ssięgają ą tych samych zmiennych są pośrednio powiąz 3. Dwie metody m zane, je eżeli po owiąe metody. zzane są ą poprz zez inne Metrryka silnej ko ohezji T TCC: Metrryka słłabej kohezji k LCC: Kla asa A NP = 10 NDC C(A) = 2, 2 NID(A A) = 2 TCC(A)=0,2 2; LCC((A)=0,2 2 TC CC = N NDC/NP P LC CC = N NID/NP P Kla asa B NDC(B N NID(B) = 6 ) = 4, N TCC(B) T )=0,4; L LCC(B))=0,6 Inne e metry yki obie o ekto owe e Wspó W ółczy ynnik he erme etycz znoś ści atrybutów w AH HF C TC ∑ Ahid (Ci ) AH HF = i =1 TC C ∑ Aall (Ci ) i =1 gdzie e: • Ahid(Ci) jest lic czbą ukkrytych atrybuttów klassy Ci • Aall(Ci) jest licz zbą wszystkic ch atryb butów k lasy Ci Niskie warto ości ws spółczyynnika AHF A ws skazują ą na licz zne zależności mię ędzymo odułowe e. Warrtości m mniejsz ze niż 9 90% ozznaczają w og gólnośc ci źle zaproje z ektowan ny syste em. Wspó W ółczy ynnik herm mety yczno ości mettod M MHF TC ∑ M hid h (Ci ) MHF = i =1 TC ∑ M all a (Ci ) e: gdzie i =1 • Mhid(Ci) jest lic czbą ukkrytych metod klasy C i • Mall(Ci) jest lic czbą wsszystkic ch meto od klasyy Ci Współczynnik hermetyczności metod MHF Class A { private int a; public metodaA() { zróbX; zróbY; zróbZ;} } MHF(A) = 0 Class B { private int b; private void metodaX (){zróbX;} private void metodaY (){zróbY;} private void metodaZ (){zróbZ;} public int metodaB() { metodaX(); metodaY(); metodaZ();} } MHF(B) = ¾ = 75% Zbyt niskie wartości współczynnika AHF mogą być symptomem złej konstrukcji kodu, zbyt wysokie - małej funkcjonalności klas. Wsp półcz zynn nik dziedzicze enia atry ybuttów A AIF i mettod M MIF TC TC T AIF F = i =1 MIF M = i=1 ∑ Ainh (Ci ) TC ∑ Aall (Ci ) i =1 ∑ M inh (Ci ) TC T ∑ M all (Ci ) i =1 e: gdzie • Ainh(Ci) jest lic czbą od dziedzic czonych h atrybu utów klasy Ci • Mhid(Ci) jest lic czbą od dziedzic czonych meto od klasy y Ci Mettryki AIF i MIF F opisu ują pod dobieństwo kla as wzdłuż łańccucha dziedzi d czenia.. Małe wartoś ści met ryk mo ogą być sympttomem przypa adkowy ych zależnoścci międ dzy klassami. W Współczy ynnik k pollimorrfizm mu PF F T TC ∑ M ovrrld (Ci ) PF = i =1 TC ∑ [M new (Ci ) × DC(Ci )] i =1 gdzie e: • Movrld(C Ci) jest liczbą rredefiniowanyc ch meto od klas sy Ci • Mnew(Ci) jest liczbą nowych metod klasy C i • DC(Ci) jest liczbą kla as potom mnych klasy C i - bez zpośśrednicch i poś średnich h Zbyt w wysoka a warto ość teg go wsp półczyn nnika (> >10%) jest sympttomem niskiej współużywalności kodu k – klasy dzied dziczą ą funkcjjonalno ość, ale e nie dz ziedzicz zą imple ementa acji. Bardzzo niska a warto ość wsp półczyn nnika (< <2%) je est sym mptomem sstatyczznego i trudneg go do utrzyma u ania ko du. W Wspó ółczy ynnik k powiąz zania a kla as TC TC C CF = ∑ ∑∑ conn(C , C ) i =1 j =1 i j C − C − 2 × ∑i =1 DC D (Ci ) 2 C gdzie e: • D DC(Ci) jest liczbą kla as potom mnych klasy C i • ccon(Ci, Cj) – repreze entuje powiąza p ania mię ędzy klasami C i i Cj o co on(Ci, Cj) = 1 je eżeli kla asy Ci i Cj są p powiąza ane zw wiązkiem m innym m niż dz ziedzic czenie, o co on(Ci, Cj) = 0, w przec ciwnym m wypad dku o rellacja co on nie je est sym metrycz zna Metrykka ta po owinna a przyjm mować jak j najm mniejszze warttości.