Witaj, Gościu O nas | Kontakt | Mapa
Wortal Forum PHPEdia.pl Planeta Kubek IRC Przetestuj się!

"OO" - Programowanie obiektowe w praktyce

Rozwiązanie obiektowe

Odpowiednik naszego poprzedniego kodu, napisany w sposób obiektowy może wyglądać tak jak poniżej. Nie przejmuj się, jeśli czegoś nie rozumiesz, ten przykład jest dość rozbudowany, ma pokazać możliwości nowego podejścia. Wrócimy do niego później. Zwróć tylko uwagę jak ułożony jest kod i porównaj go z tym u góry, napisanym w starym stylu.

class DaneUzytkownika
{
 // nasza wlasciwosc, chroniona przed dostepem z zewnatrz
 protected $dane = array();
 
 function __construct($dane = array()) 
 {
 if (is_array($dane))
 $this->dane = $dane;
 else if ($dane instanceof DaneUzytkownika)
 $this->dane = $dane->pobierzJakoTablica());
 }

 // zwraca atrybut
 function __get($parametr)
 {
 if (isset($this->dane[$parametr]))
 return $this->dane[$parametr];
 return '';
 }

 // ustawia atrybut
 function __set($parametr, $wartosc)
 {
 $this->dane[$parametr] = $wartosc;
 }

 // zwraca dane w postaci listy par klucz='wartosc'
 // oddzielonych przecinkiem
 function pobierzListeDanych()
 {
 $list = '';
 foreach($this->dane as $atrybut => $wartosc)
 $list .= $atrybut.'\=''.addslashes($wartosc).'\'';
 return $list;
 }

 // zwraca listę atrybutow oddzielonych przecinkiem
 function pobierzListeAtrybutow()
 {
 return join(', ', array_keys($this->dane));
 }

 // zwraca listę wartości oddzielonych przecinkiem
 function pobierzListeWartosci()
 {
 $values = array();
 foreach($this->dane as $wartosc)
 $values[] = '\''.addslashes($wartosc).'\'';
 return join(', ', $values);
 }

 // zwraca dane w postaci tablicy (zwraca przez wartosc)
 function pobierzJakoTablica() 
 {
 return $this->data;
 } 
}

class Uzytkownik extends DaneUzytkownika
{
 private $dane_w_bazie;

 // konstruktor - tworzenie obiektu
 function __construct(&$dane, $baza) 
 {
 parent::__construct();
 
 if ($this->dane) 
 $this->dane_w_bazie = new DaneUzytkownika($this->dane);
 }

 function wyswietl()
 {
 include_once 'Smarty.class.php';
 $smarty = new Smarty; 
 $smarty->assign('user', $this->dane);
 $smarty->display('o_uzytkowniku.tpl');
 }

 function zapisz()
 {
 mysql_query('UPDATE user SET '.$this->pobierzListDanych().' WHERE '.$this->dane_w_bazie->pobierzListeDanych()); 
 if (mysql_affected_rows()) 
 { 
 $this->dane_w_bazie = $this->dane;
 return true;
 }
 return false;
 }

 function usun()
 {
 mysql_query('DELETE FROM user WHERE '.$this->pobierzListeDanych());
 return (bool) mysql_affected_rows();
 }
}

class BazaUzytkownikow
{
 function pobierz(DaneUzytkownika $szukamy) 
 {
 $query = mysql_query('SELECT * FROM user WHERE '.$szukamy->pobierzListeDanych()); 
 if (mysql_num_rows($query))
 return new Uzytkownik(mysql_fetch_assoc($query));
 return false;
 }

 function dodaj(DaneUzytkownika $dane)
 {
 mysql_query('INSERT INTO user('.$dane->pobierzListeAtrybutow().') VALUES('.$dane->pobierzListeWartosci().')';
 if (mysql_affected_rows())
 return new Uzytkownik($dane, $this);
 return false;
 }

 function zapisz(DaneUzytkownika $dane)
 {
 if ($dane instanceof Uzytkownik)
 $dane->zapisz();
 else
 $this->dodaj($dane);
 }

 function usun(Uzytkownik $uzytkownik)
 {
 return $uzytkownik->usun();
 }
}

Obszerne? Może i tak, ale zobacz jak wspaniale się tego używa:

// najpierw przygotujmy sobie bazę użytkowników
$baza = new BazaUzytkownikow;

// teraz przykładowo szukamy użytkownika można to zrobić tak:
$szukamy = new DaneUzytkownika; // tworzymy nową jakby tablicę na dane
$szukamy->login = 'Nalfein'; // podajemy login szukanego uzytkownika
$szukamy->username = 'Adam Dziendziel'; // mozemy takze podac inny parametr

$uzytkownik = $baza->pobierz($szukany); // wyszukujemy w bazie przekazując jej dane z tablicy, baza zwraca nam Użytkownika
$uzytkownik->wyswietl() // wyświetlamy szablon Smarty

// zmieniamy dane pobranego wcześniej użytkownika
$uzytkownik->username = 'Tajemniczy gościu';
$uzytkownik->zapisz();

// dodajemy nowego użytkownika pobierając dane wysłane z formularza
$dane_nowego = new DaneUzytkownika;
$dane_nowego->login = $_POST['login'];
$dane_nowego->username = $_POST['username'];
$nowy = $baza->dodaj($dane_nowego);

// rozmyślilismy się i usuwamy nowododanego ludka
$nowy->usun();
// lub (jak komu pasuje)
$baza->usun($nowy);

Fajne nie? Bardzo intuicyjne i wygodne w użyciu. Zdziwiłbyś się jak łatwo można to rozbudować o obsługę innych rekordów - artykułów, newsów i tak dalej. Wystarczy dodać kilkanaście linijek i możemy tego samego interfejsu używać wszędzie gdzie się nam podoba. Ale o tym później, omówmy najpierw powyższy kod, ten gdzie mamy naszą pierwszą, w pełni obiektową biblioteczkę. Jest on podzielony na jakby trzy części, trzy jednostki zwane klasami (ang. class). Klasa są odpowiednikiem modelu, projektu wg. którego tworzymy obiekt za pomocą słowa kluczowego new (patrz druga linijka ostatniego listingu). Klasy są jakby foremkami, z których może powstać nieskończona liczba identycznych ciastek, obiektów. Tak jak każdą foremkę charakteryzują właściwości (kształt) i sposób użycia, tak naszą klasę definiują właściwości (zwane także atrybutami) i metody.

Właściwościami są po prostu zmienne, które są związane z naszą klasą np. w przypadku DanychUżytkownika jest to... tablica $dane w której przechowujemy, tak, tak, nic innego jak dane :) Ma ona znacznik protected, który oznacza, że jest chroniona przed dostępem z zewnątrz, możemy jej używać tylko w naszej klasie i klasach potomnych, dziedziczących z naszej, rozszerzających naszą klasę (z klasy DaneUzytkownika dziedziczy klasa Uzytkownik - o dziedziczeniu później). Znacznik private, który pojawia się w klasie Uzytkownik oznacza, że jest to zmienna pomocnicza, używana tylko przez naszą klasę i żadna klasa dziedzicząca z naszej (ani tym bardziej użytkownik z zewnątrz) nie powinna mieć do niej dostępu. Istnieje jeszcze modyfikator dostępu public (możemy go zastąpić przez słowo kluczowe var), który mówi, że właściwość ta jest publiczna, możemy ją zmieniać z zewnątrz jak i wewnątrz klasy naszej i wszystkich jej potomków (klas wywodzących się z naszej).

Nasza klasa ma także metody, czyli inaczej funkcje działające na rzecz klasy, wykonujące operacje związane z naszą klasą i operujące na danych, właściwościach naszego obiektu. Pierwszą taką funkcją, automatycznie wywoływaną przy tworzeniu obiektu jest konstruktor, metoda o nazwie __construct(). Podobnie istnieje metoda wywoływana przy usuwaniu obiektu z pamięci (np. gdy skrypt kończy wykonywanie), zwana destruktorem, o analogicznej nazwie __destruct(), tutaj jej nie mamy, ale może ona służyć np. do zamykania plików lub połączeń z bazą danych. Mamy tutaj za to dwie inne metody, wywoływane niejawnie __get() i __set() są one używane przy pobieraniu i ustawianiu właściwości, których nie zadeklarowaliśmy tzn. nie umieściliśmy jawnie wpisu w klasie, że tak się nazywają. Jest to "bajer" wprowadzony z PHP5, zwany przeciążaniem właściwości, tutaj użyłem go dla wygody. Gdy w kodzie napiszemy:

$uzytkownik->login = 'Nalfein';

To - jeśli istnieje metoda __set() to zostanie wywołana z argumentami 'login' i 'Nalfein'. Analogicznie jest z metodą __get() i pobieraniem. Jak już pisałem, zadeklarowaliśmy, jawnie podaliśmy w klasie DaneUzytkownika, że będziemy używać właściwości $dane. Używamy jej tylko wewnątrz klasy m.in. właśnie w metodach __get() i __set(), zapisując i odczytując z niej dane naszego użytkownika, dlatego też ograniczyliśmy jej dostęp z zewnątrz, ukrywając ją. Nie możemy napisać:

$uzytkownik->dane = array('login' => 'Nalfein');

chyba, że zmienimy słowo protected na public, lub po prostu na var (pozostałość z PHP4). Takie ukrywanie zwane jest fachowo enkapsulacją lub hermetyzacją i jest robione po to, aby zminimalizować ryzyko nieprawidłowego użycia obiektu. Poza tym, po co użytkownik naszej klasy ma wiedzieć jak przechowujemy dane. Możemy je pobierać nawet z pliku XML i przy pobieraniu właściwości parsować go (rozdzielać znaczniki) po czym zwracać odpowiedni. Dla niego istotne jest to, jaką funkcję ma pełnić dany obiekt (klasa), czyli, za co ma być odpowiedzialny i w jaki sposób będziemy go używać tzn. jakie metody możemy wywoływać (i co one powinny robić) oraz do jakich właściwości mamy dostęp, to znaczy jakie dane obiektu możemy zmieniać bezpośrednio, przypisując poszczególnym zmiennym składowym (inna nazwa właściwości) dowolne wartości.

W metodach pojawia się słowo kluczowe $this, jest to referencja do obiektu, używamy jej, aby dobrać się do składowej (zmiennej czy metody) z wewnątrz klasy. Używamy jej tak samo jak innych obiektów na zewnątrz klasy - wykorzystując operator "strzałki" (->) . Po niej może nastąpić nazwa metody, którą chcemy wywołać lub właściwości, której wartość chcemy zmienić lub pobrać. Pojawia się także słowo kluczowe parent, używamy go, aby uzyskać dostęp do klasy (nie ma $ przed "parent" - odwołujemy się więc nie do obiektu, a do klasy) rodzica, klasy z której dziedziczy nasza klasa. Potrzebujemy go, aby móc wywołać metodę przesłoniętą (nadpisaną), czyli taką, która choć istniała w klasie rodzica (DaneUzytkownika), została ponownie zdefiniowana w klasie potomnej (Uzytkownik). Taką metodą tutaj jest konstruktor, który jak już pisałem ma specjalne właściwości, jest wywoływany przy tworzeniu obiektu i jest często nadpisywany, ponieważ stosujemy go do inicjalizacji zmiennych składowych tworzonego obiektu. Rozszerzamy klasę, chcemy więc zainicjować także nowe właściwości, które dodaliśmy. Dlatego piszemy nowy konstruktor i wykonujemy tam wszystkie niezbędne czynności, w pierwszej linijce najczęściej jednak wywołujemy "stary" konstruktor, bo nie możemy zapomnieć o naszej klasie bazowej, tej rozszerzanej, która także może ustawiać początkowe wartości dla swoich wlaściwości. Nie możemy wprost napisać

$this->__construct();

bo byłoby to rekurencyjne wywoływanie tej samej funkcji, tego "nowego" konstruktora. Aby dać znać PHP, że ma wywołać "stary" konstruktor piszemy:

parent::__construct();

Podobnie jak konstruktor można przeciążać (przesłaniać) inne metody, zmieniając ich zachowanie, w razie potrzeby wywołując metodę rodzica.

W jednej z linijek pojawia się kod:Klas

if ($dane instanceof Uzytkownik) { .... }

Operator instanceof zwróci prawdę, jeśli obiekt po lewej stronie jest instancją klasy Uzytkownik lub klasy potomnej. Sprawdzamy tutaj, czy przekazano tylko DaneUzytkownika czy już obiekt-opakowanie dla rekordu z bazy, naszego Uzytkownika. Jeślibyśmy zmienili w tej instrukcji warunkowej "Uzytkownik" na "DaneUzytkownika" to operator ten zwróci prawdę zarówno, gdy przekażemy jej DaneUzytkownika jak i samego Uzytkownika, gdyż klasa Uzytkownik rozszerza klasę DaneUzytkownika.

Informacje na podobny temat:
Wasze opinie
Wszystkie opinie użytkowników: (3)
:))
Piątek 11 Kwiecień 2008 2:45:35 pm - lonas

Bardzo fajne ale ... czemu takie krotkie wiecej !! :)

koment
Czwartek 26 Lipiec 2007 10:46:57 pm - plurr <xytrass_at_o2.pl>

bardzo fajny artykul, nareszcie jasniej zrozumialem klasy ;p Tylko gdzie reszta ? :))

To są podstawy?
Niedziela 22 Lipiec 2007 10:36:39 am - solari_de_marco <solari_de_marco_at_interia.pl>

Kurs niby podstawy a .... Potrafię dobrze programować strukturalnie i chciałem się nauczyc OO i mi to ciężko idzie... :/ Myślałem ze tu się czegoś nauczy, niby oki ale ... jak mam się połączyć z bazą danych? Gdzie jest wyjasnienie ze trzeba wybrac baze danych, połączyc sie i itp, jak podajesz kod wykożystania klas to już pokaż jak się łaczyć z tą bazą danych !!

Mentax.pl    NQ.pl- serwery z dodatkiem świętego spokoju...   
O nas | Kontakt | Mapa serwisu
Copyright (c) 2003-2020 php.pl    Wszystkie prawa zastrzeżone    Powered by eZ publish Content Management System eZ publish Content Management System