PHP Lekcije - lekcija 13 - Osnove OOP (objektno-orijentisanog programiranja)
Na prethodnim lekcijama sam već pisao o sledećim tipovima podataka u PHP-u: logički, celobrojni, decimalni, tekstualni, nizovi. U ovoj lekciji ću govoriti o još jednom tipu podataka dostupnom u PHP-u - objektu.
Objekti su donekle slični nizovima, objekti mogu sadržavati različite tipove podataka kao i nizovi: brojeve, tekst, nizove pa čak i druge objekte.
Objekti mogu sadržavati druge tipove podataka, kao nizovi. Te podatke nazivamo svojstvima (poljima) objekta. Takođe, objekti mogu sadržati funkcije u sebi, te funkcije nazivamo metodima.
Ali objekti imaju dva velika razlikovanja u odnosu na nizove:
1. Objekti mogu sadržati metode.
Metode su funkcije koje se pozivaju u ime objekta. Veza funkcija sa objektom omogućava označavanje koje operacije objekat može izvršavati i operacije koje se rade nad objektima.
2. Objekti se kreiraju iz klasa.
Pre nego što napravimo objekat, moramo definisati neku klasu. U klasi opisujemo koja polja i metode objekat ima. To omogućava definisanje strukture objekata i olakšava razumevanje koje podatke objekat treba da čuva i koje operacije izvodi. Prilikom kreiranja objekta iz klase, možemo popuniti objekat početnim podacima, tako da svi novo kreirani objekti nisu prazni.
Objekat je verovatno najpraktičniji kontejner za čuvanje podataka. Objekti u PHP-u pomažu da se modeluju objekti iz stvarnog sveta, na primer, imamo bazu podataka u kojoj čuvamo informacije o knjigama. Knjiga ima autora, broj stranica, izdavača, broj izdanja, recenziju, tipografske podatke. Zbog praktičnosti rada sa podacima, grupišemo ih u klasu Knjiga, koja ima svojstva Autor, Broj stranica, Izdavač, Broj izdanja, Recenzija, Tipografski podaci itd. Na taj način kreiramo objekte koji liče na prave knjige. Ovo je posebno korisno jer podatke sa sajtova obično čuvamo u tabelama sa kolonama, pa možemo nazvati tabele kao klase, a kolone kao imena svojstava klase. Rezultat je značajno olakšan rad sa podacima.
Detaljnije ćemo razmotriti razliku između nizova i objekata kroz primere.
Mogućnosti PHP 5.2 i novije verzije:
Počnimo definisanjem klase naših objekata. Obratite pažnju da je klasa u jednini, a objekti u množini, čime želim da naglasim da iz jedne klase možemo napraviti mnogo objekata:
<?php class book{ } ?>
Korišćenjem ključne reči class definišemo klasu za buduće objekte, a sada hajde da napravimo objekte:
<?php class book{ } $book1 = new book; $book2 = new book; print_r($book1); print_r($book2); ?>
Korišćenjem ključne reči new pravimo nove objekte klase book. Svaki objekat klase nazivamo instancom klase. Tako imamo dve instance klase book, $book1 i $book2. Takođe smo dodali funkciju print_r da bi prikazali sadržaj promenljivih $book1 i $book2 na ekranu, i dobijamo:
Kao što vidite, PHP prikazuje iz koje klase je objekat kreiran, i da su podaci u objektu prikazani ukoliko postoje. Sada dodajmo te podatke. Prvo treba definisati svojstva klase. Počevši od PHP 5.2, svojstva se definišu koristeći tri ključne reči: public, protected, private (stari način je var, o tome kasnije). Ako ste radili u Delphi-ju ili C++, ovo će vam biti poznato jer ove ključne reči funkcionišu isto kao i u tim jezicima.
public - definiše javna (otvorena) svojstva i metode klase. Do ovih svojstava se može pristupiti spolja preko objekta. Ovako ćemo definisati sva svojstva za podatke o knjizi.
protected - definiše zaštićena svojstva i metode. Takvim svojstvima i metodama može se pristupati samo unutar klase i unutar izvedenih (nasleđenih) klasa. Ako se pokuša pristupiti protected metodu sa spoljne instance objekta, dobiće se greška. O nasleđivanju i protected pristupu ćemo detaljnije kasnije.
private - definiše privatna (zatvorena) svojstva i metode. Takvim se može pristupiti samo unutar iste klase i nigde drugde, čak ni u izvedenim klasama.
Za sada ćemo sve definisati kao public da ne komplikujemo.
class book{ public $author; public $numberOfPages; public $year; public $publishingOffice; public $editionNumber; }
Sada imamo definisana svojstva klase, što nam omogućava da u instancama pristupamo tim svojstvima:
class book{ public $author; public $numberOfPages; public $year; public $publishingOffice; public $editionNumber; } $book1 = new book; $book1->author = 'Abramenko I.A'; $book1->numberOfPages = 192; $book1->year = 2013; $book1->publishingOffice = 'Moscow'; $book1->editionNumber = 1; $book2 = new book; print '<pre>'; print_r($book1); print_r($book2); print '</pre>';
Rezultat su jedan popunjen objekat i jedan prazan:
Sada je jasno kako upisivati podatke u objekte. Hajde da sada objasnimo metode klase. Kao što sam ranije napisao, metode su funkcije koje se pozivaju preko objekta ili klase. Dodajmo metode za dohvatanje (getere) svakog svojstva:
<?php class book{ public $author; public $numberOfPages; public $year; public $publishingOffice; public $editionNumber; public function getAuthor(){ return $this->author; } public function getNumberOfPages(){ return $this->numberOfPages; } public function getYear(){ return $this->year; } public function getPublishingOffice(){ return $this->publishingOffice; } public function getEditionNumber(){ return $this->editionNumber; } } $book1 = new book; $book1->author = 'Abramenko I.A'; $book1->numberOfPages = 192; $book1->year = 2013; $book1->publishingOffice = 'Moscow'; $book1->editionNumber = 1; $book2 = new book; print '<pre>'; print $book1->getAuthor() . '<br />'; print $book1->getNumberOfPages() . '<br />'; print $book1->getYear() . '<br />'; print $book1->getPublishingOffice() . '<br />'; print $book1->getEditionNumber() . '<br />'; print '</pre>'; ?>
Na ovaj način pristupamo objektu putem metoda, a ne direktno svojstava. Obratite pažnju na promenljivu $this. Njena vrednost odgovara vrednostima trenutne instance klase. Ako je instanca $book1, onda su sve vrednosti $book1 dostupne kroz $this unutar klase. Tako možemo koristiti $this za rad sa promenljivim unutar klase. Izvan klase $this nije dostupan. Hajde da sada dodamo još dva metoda za prikaz knjige u obliku tabele i u div-u.
class book{ public $author; public $numberOfPages; public $year; public $publishingOffice; public $editionNumber; public function getAuthor(){ return $this->author; } public function getNumberOfPages(){ return $this->numberOfPages; } public function getYear(){ return $this->year; } public function getPublishingOffice(){ return $this->publishingOffice; } public function getEditionNumber(){ return $this->editionNumber; } public function displayTable(){ $content = '<table style="border: 1px solid #000">'; $content .= '<tr><td>Author</td><td>'. $this->getAuthor() . '</td></tr>'; $content .= '<tr><td>Number of pages</td><td>'. $this->getNumberOfPages() . '</td></tr>'; $content .= '<tr><td>Year</td><td>'. $this->getYear() . '</td></tr>'; $content .= '<tr><td>Publishing office</td><td>'. $this->getPublishingOffice() . '</td></tr>'; $content .= '<tr><td>Edition number</td><td>'. $this->getEditionNumber() . '</td></tr>'; $content .= '</table>'; return $content; } public function displayUnformatted(){ $content = '<div style="border:1px solid #ddd">'; $content .= '<label>Author:</label><div>'. $this->getAuthor() . '</div>'; $content .= '<label>Number of pages:</label><div>'. $this->getNumberOfPages() . '</div>'; $content .= '<label>Year:</label><div>'. $this->getYear() . '</div>'; $content .= '<label>Publishing office:</label><div>'. $this->getPublishingOffice() . '</div>'; $content .= '<label>Edition number:</label><div>'. $this->getEditionNumber() . '</div>'; $content .= '</div>'; return $content; } } $book1 = new book; $book1->author = 'Abramenko I.A'; $book1->numberOfPages = 192; $book1->year = 2013; $book1->publishingOffice = 'Moscow'; $book1->editionNumber = 1; $book2 = new book; $book2->author = 'Leshkina S.O.'; $book2->numberOfPages = 200; $book2->year = 2013; $book2->publishinOffice = 'Moscow'; $book2->editionNumber = 1; print $book1->displayTable(); print $book2->displayUnformatted();
Imamo dosta koda, ali kao što vidite, prilično je čitljiv. Zaboravio sam da dodam naziv knjige, hajde da dodamo još jedno svojstvo name i get metodu:
<?php class book{ public $author; public $name; // zaboravio sam ovo public $numberOfPages; public $year; public $publishingOffice; public $editionNumber; public function getAuthor(){ return $this->author; } public function getName(){ return $this->name; } public function getNumberOfPages(){ return $this->numberOfPages; } public function getYear(){ return $this->year; } public function getPublishingOffice(){ return $this->publishingOffice; } public function getEditionNumber(){ return $this->editionNumber; } public function displayTable(){ $content = '<table style="border: 1px solid #000">'; $content .= '<tr><td>Author</td><td>'. $this->getAuthor() . '</td></tr>'; $content .= '<tr><td>Name</td><td>'. $this->getName() . '</td></tr>'; $content .= '<tr><td>Number of pages</td><td>'. $this->getNumberOfPages() . '</td></tr>'; $content .= '<tr><td>Year</td><td>'. $this->getYear() . '</td></tr>'; $content .= '<tr><td>Publishing office</td><td>'. $this->getPublishingOffice() . '</td></tr>'; $content .= '<tr><td>Edition number</td><td>'. $this->getEditionNumber() . '</td></tr>'; $content .= '</table>'; return $content; } public function displayUnformatted(){ $content = '<div style="border:1px solid #ddd">'; $content .= '<label>Author:</label><div>'. $this->getAuthor() . '</div>'; $content .= '<label>Name:</label><div>'. $this->getName() . '</div>'; $content .= '<label>Number of pages:</label><div>'. $this->getNumberOfPages() . '</div>'; $content .= '<label>Year:</label><div>'. $this->getYear() . '</div>'; $content .= '<label>Publishing office:</label><div>'. $this->getPublishingOffice() . '</div>'; $content .= '<label>Edition number:</label><div>'. $this->getEditionNumber() . '</div>'; $content .= '</div>'; return $content; } } $book1 = new book; $book1->author = 'Abramenko I.A'; $book1->name = 'Drupal: User guide'; $book1->numberOfPages = 192; $book1->year = 2013; $book1->publishingOffice = 'Moscow'; $book1->editionNumber = 1; $book2 = new book; $book2->author = 'Leshkina S.O.'; $book2->name = 'Drupal: Fields, Images, Views'; $book2->numberOfPages = 200; $book2->year = 2013; $book2->publishinOffice = 'Moscow'; $book2->editionNumber = 1; print $book1->displayTable(); print $book2->displayUnformatted(); ?>
Mogućnosti PHP 5.1.3 i starije verzije:
Ako želimo da definišemo svojstva (promenljive) unutar klase, koristimo reč var.
<?php
class myClass{
}
?>
Hajde da pogledamo primer sa nizovima i funkcijama:
<?php $newArray =array(1,2,3,4,5); function _increment_all_elements($customArray){ foreach($customArray as $key){ $customArray[] = $key+1; } return $customArray; } $newArray = _increment_all_elements($newArray); print_r($newArray); ?>
Primer sam po sebi nema mnogo smisla, dodali smo u postojeći niz njegove elemente povećane za 1. Ali važno je kako smo to uradili - pomoću funkcije, što je osnova funkcionalnog programiranja u PHP-u. Objekti zauzimaju posebno mesto i programiranje sa njima se naziva objektno-orijentisano programiranje.
Kasnije ćemo se vratiti na ovaj primer i uraditi ga pomoću objekta, ali sada treba da shvatimo šta je objekat.
Objekti, kao i funkcije, omogućavaju vezivanje funkcija i podataka, ali imaju izgled bliži stvarnom svetu. Na primer, objekat proizvod može imati svojstva cena, težina, širina, dužina. Bliža realnosti olakšava razumevanje koda.
Objekti se (obično) kreiraju kao instance neke klase, na primer objekat "lopta" može biti instanca klase "proizvod". Klasa je generalizacija sa opisom svojstava i funkcija, dok je objekat realizacija klase.
Hajde da napravimo našu prvu klasu i objekat:
<?php class WorkWithArray{ } ?>
Korišćenjem reči class pravimo klasu, unutar viticastih zagrada pišemo svojstva i funkcije klase. U klasama se funkcije nazivaju metodama, pa ću ubuduće koristiti termin metod za funkciju u klasi. Isto važi i za promenljive u klasama, koje nazivamo svojstvima klase.
Dodajmo prvo svojstvo našem objektu, odnosno niz:
<?php class WorkWithArray{ var $myArray = array(); } ?>
Sada klasa za rad sa nizovima ima svojstvo niz, svojstva klase se još nazivaju promenljive instance ili članovi podataka. Dodavanje svojstava klase vrši se pomoću reči var.
Bilo bi korisno dodati i funkciju klasi:
<?php class WorkWithArray{ var $myArray = array(); function increment_all_elements($newArray){ foreach($newArray as $key){ $newArray[] = $key+1; } return $newArray; } } ?>
Kao što vidite, funkcije se dodaju klasi kao obične funkcije, ništa neobično. Napravili smo klasu, hajde da je sada koristimo i napravimo objekat.
<?php $myObject = new WorkWithArray; print_r($myObject); ?>
Štampamo objekat pomoću print_r() kao što smo radili sa nizovima. Sada kada smo napravili objekat, pristupimo mu. Prema definiciji klase, imamo jedno svojstvo myArray i jedan metod increment_all_elements():
<?php class WorkWithArray{ var $myArray = array(); function increment_all_elements($newArray){ foreach($newArray as $key){ $newArray[] = $key+1; } return $newArray; } } $myObject = new WorkWithArray; $myObject->myArray = array(2,4,6,7); print_r($myObject); ?>
Pristupamo svojstvima objekta preko znaka strelice ->, što se čita kao "objektu myObject pripada svojstvo myArray".
Sada možemo operisati sa ovim svojstvom koristeći definisani metod:
<?php class WorkWithArray{ var $myArray = array(); function increment_all_elements($newArray){ foreach($newArray as $key){ $newArray[] = $key+1; } return $newArray; } } $myObject = new WorkWithArray; $myObject->myArray = array(2,4,6,7); $myObject->myArray = $myObject->increment_all_elements($myObject->myArray); print_r($myObject); ?>
Pristup metodama objekta je isti kao i pristup svojstvima, preko ->. Pozivanje metoda se zove "poziv". Pozvali smo metod increment_all_elements, kao parametar prosledili svojstvo objekta, i to svojstvo smo potom prepisali izmenjenim nizom. Rezultat je izmenjeni niz podataka.
Ovo je jednostavan primer kako se prave objekti i kako im se pristupa. Dalje ćemo detaljnije razmatrati objektno-orijentisano programiranje.