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.

Podobne dokumenty