Wstęp do programowania obiektowego
Wykład 2
1
CECHY I KONCEPCJA
PROGRAMOWANIA
OBIEKTOWEGO
Cechy programowania obiektowego
Dla wielu problemów podejście obiektowe jest zgodne z rzeczywistością (łatwe do
przyswojenia przez człowieka; dobrze odzwierciedlające sposób, w jaki ludzie myślą o świecie).
PO nie daje żadnych nowych efektów w gotowym programie
Zastosowanie PO nie jest widoczne dla użytkownika
Zastosowanie PO może pogorszyć wydajność obliczeniową programu (w porównaniu do analogicznego programu strukturalnego)
3
C++ jest językiem zarówno obiektowym, jak i imperatywnym/strukturalnym/proceduralnym.
Języki programowania z cechami obiektowości to m.in.: C#, Java, Object Pascal, Lisp, Ruby,
Python, PHP5.
Koncepcja programowania obiektowego
Otaczający nas świat to różnego rodzaju przedmioty.
Tworząc program komputerowy dokonujemy pewnego ich odwzorowania.
W programowaniu proceduralnym funkcje i
zmienne dotyczące danego przedmiotu nie są ze sobą logicznie powiązane.
W programowaniu obiektowym dokonuje się powiązania metod (funkcji programu) z danymi (zmiennymi) definiującymi przedmiot.
Wszystko to grupuje się w klasie zawierającej w jednym miejscu zarówno dane definiowanego przedmiotu, jak i dotyczące go funkcje.
5
Podstawowe definicje
Klasa – (częściowa lub całkowita) definicja dla obiektu - opis budowy i działania obiektów
Obiekt – fizyczna instancja (ucieleśnienie, egzemplarz) klasy. Jest to struktura zawierająca:
dane
metody, czyli funkcje służące do wykonywania na tych danych określonych zadań.
Każdy obiekt ma trzy cechy:
tożsamość, czyli cechę umożliwiającą jego identyfikację i odróżnienie od innych obiektów;
stan, czyli aktualny stan „własnych” danych;
zachowanie, czyli zestaw metod wykonujących operacje na tych danych.
UKRYWANIE IMPLEMENTACJI:
HERMETYZACJA (ENKAPSULACJA)
7
Hermetyzacja
„Zwykłemu użytkownikowi” niepotrzebna jest znajomość wewnętrznej budowy urządzeń.
Do codziennej obsługi, wystarczą nam udostępnione
bezpieczne manipulatory. Mamy dostęp tylko do wybranych metod urządzenia (np. manipulatory telewizora). W ten
sposób część (bardziej skomplikowanych) funkcji jest przed nami ukryta.
Analogicznie jest w programowaniu obiektowym:
Hermetyzacja (enkapsulacja) - ograniczenie
bezpośredniego dostępu do niektórych pól obiektów,
ewentualnie umożliwienie ich modyfikacji poprzez metody.
Specyfikatory (etykiety) dostępu
O dostępności pól i metod decydują tzw. specyfikatory dostępu: public, private i protected.
W C++ w ciele klasy deklaruje się sekcje (zawierające jedną lub więcej składowych):
public – składowe widoczne globalnie, dla wszystkich, np. z maina;
private – składowe widoczne tylko dla danej klasy;
protected – składowe widoczne tylko dla danej klasy oraz klas dziedziczących.
class Punkt { public:
int x, y;
double odległość (int xx, int yy) {return sqrt((x-xx)*(x-xx)+(y- yy)*(y-yy));}
private:
void zeruj () {x = 0; y = 0;}
protected:
double przesuń (int dx, int dy) {x+=dx; y+=dy;}
}; 9
Etykieta dotyczy występujących po niej metod i pól.
Liczba etykiet jest dowolna (mogą się powtarzać).
W przypadku braku etykiety domyślnie jest przypisana sekcja prywatna.
class Punkt{
void fun1(){…} //prywatna, bo domyślna
protected:
int fun2(){ … } int y;
private:
float fun3(){…}
int x;
public:
double fun4(){…}
};
Konwencja nazewnictwa get/set/is
Kiedy pole jest zadeklarowane jako prywatne możemy umożliwić :
odczyt wartości pola przez metodę publiczną nazywaną get<nazwa pola>().
modyfikację wartości pola przez metodę publiczną nazywaną set<nazwa pola>.
class Punkt {
private:
int x;
int y;
public:
int getx(){return x;}
int gety(){return y;}
void setx(int xx){x=xx;}
void sety(int yy){
if (yy>=0) y=yy;}
};
11
W C++ jest to tylko konwencja
nazewnictwa (zalecany sposób tworzenia nazw metod do odczytu i zapisu pól
prywatnych), a nie obowiązkowy element języka.
Stosuje się to w wielu językach obiektowych, niekiedy jest to też
mechanizm wbudowany (jak np. akcesory set, get w C#).
Korzyści z hermetyzacji
Funkcje getx(), gety(), setx() i sety() znajdują się w
sekcji public i są widoczne poza obiektem klasy. Pola x i y nie są widoczne poza obiektem klasy, ale jedynie wewnątrz obiektu (mają do niej dostęp metody klasy, czyli m.in. nasze funkcje getx(), gety(), setx() i sety() ).
Z tego względu te funkcje stanowią "interfejs"
dostępu do pól obiektu klasy Punkt.
Daje to nam kontrolę nad zapisem i odczytem do pól x i y poprzez odpowiednie definicje metod (możemy sprawdzić, odrzucić czy zabronić modyfikacji pola).
Standardowe nazwy takich metod ułatwiają czytanie kodu.
Można wyświetlić zmiany użytkownikowi w
odpowiedzi na zmianę wartości zmiennej (wywołanie settera powoduje też wyświetlenie tekstu lub
aktualizację kontrolki GUI)
13
Dwie role programistów PO
• Twórca klas – tworzy biblioteki kodu do
wykorzystania, np. klasy, ich metody i dane
• Programista-klient – używa stworzonego kodu, czyli gotowych klas: tworzy obiekty, wywołuje metody itp.
Dobrze jest ukryć przed programistą-klientem nieistotne dla niego szczegóły implementacji, które mogłyby być niewłaściwie użyte. W ten sposób zmniejsza się ryzyko popełnienia przez niego błędów w używaniu gotowych bibliotek
(klas). Takie postępowanie to właśnie ukrywanie implementacji (hermetyzacja).
Modyfikacje bibliotek
Ukrywanie implementacji pozwala też na modyfikację przez twórcę klas szczegółów udostępnianych bibliotek, bez większego wpływu na kod wykorzystujący te
biblioteki (kod napisany przez programistę-klienta).
Modyfikacje to na przykład rozszerzanie funkcjonalności biblioteki czy
optymalizacja kodu.
15
KONSTRUKTORY I
DESTRUKTORY
Geneza
Przyczyną wielu błędów jest to, że programista zapomina o inicjalizacji
zmiennych lub "końcowych porządkach".
W programowaniu obiektowym
wprowadzono więc półautomatyczny mechanizm inicjacji i "sprzątania":
odpowiednio: konstruktory i destruktory.
17
Konstruktor
Przy tworzeniu obiektu zawsze
wywoływany jest konstruktor (specjalna funkcja składowa klasy).
Podstawowym zadaniem konstruktora jest inicjalizacja (nadanie odpowiednich wartości) polom tworzonego obiektu.
Cechy konstruktora:
Nazwa konstruktora jest taka sama jak nazwa klasy;
nie zwraca żadnego wyniku;
może przyjmować parametry;
Parametrami konstruktora nie mogą być obiekty klasy do której należy, ale mogą być referencje do tych obiektów (w C++);
Nie może być wywołany jak zwykła metoda (bez tworzenia obiektu);
Najczęściej jest publiczny.
19
Dwa sposoby inicjalizacji pól w C++
1. W konstruktorze przy użyciu instrukcji przypisania, np.:
Punkt(int xx, int yy) { x = xx; y = yy;
}
2. W konstruktorze, przy użyciu listy inicjacyjnej, np.:
Punkt(int xx, int yy) : x(xx), y(yy) {}
Dopuszczalne są też rozwiązania mieszane.
Konstruktor domyślny
W momencie tworzenia obiektu zawsze wywoływany jest jakiś konstruktor.
Jeżeli w klasie nie został zdefiniowany
żaden konstruktor, to kompilator dodaje (niewidoczny w kodzie) konstruktor
domyślny (bezparametrowy).
21
Konstruktor bezparametrowy
Często też definiuje się własny
konstruktor bezparametrowy. Konstruktor bezparametrowy najczęściej nadaje
polom wartości domyślne.
Tworzenie obiektów z
wykorzystaniem konstruktorów:
Punkt p(20,40), p1, p2;
Punkt* wskp1 = new Punkt(30, 35);
Punkt* wskp2 = new Punkt();
Punkt* wskp3 = new Punkt;
23
Konstruktor kopiujący
Zadaniem konstruktora kopiującego jest
utworzenie nowego obiektu jako dokładnej kopii obiektu istniejącego. Nagłówek zwykle ma postać:
<nazwa klasy>(const <nazwa klasy>&) Przykład:
Prostokąt(const Prostokąt& p)
Przy braku definicji konstruktora kopiującego, kompilator dodaje domyślny konstruktor
kopiujący, który kopiuje „pole po polu” (jest to tzw. kopia płytka i niekiedy to nie wystarcza).
Destruktor
W momencie niszczenia obiektu (np.: koniec zakresu widoczności obiektu lokalnego) wywoływany jest destruktor – zdefiniowany w klasie lub domyślny.
Celem definiowania destruktora może być np. zwolnienie zaalokowanej ręcznie pamięci, używanych zmiennych dynamicznych, itp. porządki.
Definicja destruktora:
~<nazwa klasy>()
{ <deklaracje zmiennych>
<instrukcje>, itp. }
Cechy destruktora:
nie ma parametrów,
nie ma typu wyniku
w klasie może wystąpić tylko jeden destruktor
nie można go wywołać jak zwykłej metody
25
DIAGRAMY KLAS W NOTACJI
UML
Schemat klasy w UML
Nazwa
Atrybuty (zmienne klasowe)
Metody (funkcje)
27
Żarówka
- bool włączona
# int moc
+ void włącz() + void wyłącz()
Aby używać typu bool należy dopisać na początku programu: #include <stdbool.h>
Symbole modyfikatorów dostępu:
- private + public
# protected
Przykłady klas (UML)
Punkt (na płaszczyźnie)
Punkt (w przestrzeni 3D)
Telewizor
Człowiek
Szafa/Pokój/Paczka
Odcinek
Wektor
Wielokąt (budowany z punktów)
Wielokąt (budowany z odcinków)