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

Podstawy - Programowanie obiektowe dla początkujących

Wstęp.

Zaczynamy, czyli proceduralne podejście do programu.

Koncepcja, czyli po co w ogóle OOP ?

Let's OOP, czyli budujemy Zoo.

class Tygrys {
	private $wiek;
	private $ubarwienie;
	private $waga;

	public function __construct($wiek, $ubarwienie, $waga) {
		$this->wiek = $wiek;
		$this->ubarwienie = $ubarwienie;
		$this->waga = $waga;
	}//

	public function getWiek() {
		return $this->wiek;
	}//

	public function getWaga() {
		return $this->waga;
	}//

	public function jedzTygrysie( $posilek ) {
		$this->waga += $posilek;
	}//
}
$Puszek = new Tygrys(10 , "w paski" , 150);   [1]

echo "Nowy tygrys waży ".$Puszek->getWaga()." kilogramów."; [2]
echo "Teraz damy mu 5 kilo mięsa:";

$Puszek->jedzTygrysie(5);    [3]

echo "Po zjedzeniu tygrys waży ".$Puszek->getWaga()." kilogramów";    [4]

Nowy obiekt tworzymy korzystając ze słowa kluczowego 'new', po którym następuje nazwa klasy, której obiekt chcemy utworzyć. Całość przypisana jest zmiennej, która od tej chwili reprezentuje ten obiekt. Zmienna ta jest określana mianem zmiennej typu Tygrys, tak samo, jak definicja $i = 5 definiuje nam zmienną typu int.Taki typ nazywany jest typem złożonym, w przeciwieństwie do typu int, który jest typem prostym. Typy złożone (między innymi obiekty) składają się z wielu zmiennych będących typami prostymi (int, float, string itp.). Każdy typ złożony może również składać się z innych typów złożonych (czyli cechami obiektu mogą być inne obiekty, wrócimy do tego niebawem).Przykładowa klasa Tygrys składa się z trzech cech będących typam prostymi: $waga oraz $wiek będących liczbami oraz łańcucha określającego umaszczenie (barwę) zwierzęcia. Początkowe wartości tym cechom można nadać albo w momencie definiowania klasy (wtedy każdy nowy obiekt tej klasy będzie miał "fabrycznie" ustawioną tę właściwość) lub też podać je jako parametr specjalnej funkcji, dostępnej w każdej klasie, zwanej konstruktorem (__construct). Jest to funkcja (metoda) wywoływana zawsze, gdy tworzony jest nowy obiekt za pomocą operatora 'new'. Po prostu podajemy je jako parametr wywołania (linia [1] w powyższym przykładzie), a w implementacji konstruktora, w definicji klasy, przypisujemy odpowiednie parametry odpowiadającym im właściwościom. Korzystamy przy tym ze słowa kluczowego $this->, po którym następuje nazwa interesującej nas właściwości. Czym jest słowo kluczowe $this-> ? To specjalna konstrukcja języka, która odwołuje się do właściwości "wewnątrz" obiektu i dotycząca tego właśnie, konkretnego obiektu, na którym operacja jest dokonywana. Jak należy to rozumieć? W przykładzie, w linijce [2], wywołujemy metodę getWaga(), będącą prostą funkcją zwracającą wagę tygrysa. Spójrzmy na jej implementację:

public function getWaga() {
		return $this->waga;
	}//

Ok, mamy definicję klasy 'Tygrys', mamy nawet jeden jej obiekt (Puszka). Do Zoo jeszcze nam bardzo daleko. Dodajmy więc Puszkowi towarzystwo: lwa Leona:

class Lew {
	private $wiek;
	private $ubarwienie;
	private $waga;
	private $grzywa;

	public function __construct($wiek, $ubarwienie, $waga , $grzywa) {
		$this->wiek = $wiek;
		$this->ubarwienie = $ubarwienie;
		$this->waga = $waga;
		$this->grzywa = $grzywa;
	}//

	public function getWiek() {
		return $this->wiek;
	}//

	public function getWaga() {
		return $this->waga;
	}//

	public function jedzLwie( $posilek ) {
		$this->waga += $posilek;
	}//
}

Lew, taki inny kot - czyli co to jest dziedziczenie

Zdefiniujmy sobie klasę 'Kot':

abstract class Kot {
	protected $wiek;
	protected $ubarwienie;
	protected $waga;
	
	public function __construct() {}  //pusta definicja - implementacja w 'potomkach'

	public function getWiek() {
		return $this->wiek;
	}//

	public function getWaga() {
		return $this->waga;
	}//

	public function jedz( $posilek ) {
		$this->waga += $posilek;
	}//

	public function spaceruj() {
		echo "Chodzę sobie po wybiegu...";
	}//
}
class Tygrys extends Kot {
	public function __construct($wiek, $ubarwienie, $waga) {
		$this->wiek = $wiek;
		$this->ubarwienie = $ubarwienie;
		$this->waga = $waga;
	}//
}//
$Puszek->getWaga();  //brak bezposredniej definicji metody getWaga() w klasie 'Tygrys'

Dziedziczenie jest również określane mianem relacji "X jest typem Y": w tym przypadku Tygrys to RODZAJ Kota, posiada te same cechy, co Kot.

W tym momencie należałoby wyjaśnić znaczenie słów private, protected oraz public, które pojawiają się w powyższych przykładach. Są to tak zwane modyfikatory dostępu, które wskazują, która z właściwości lub metod będzie widoczna z każdego miejsca w kodzie (public), która będzie widoczna jedynie dla 'potomków' (protected), a która będzie widoczna jedynie dla innych składowych elementów w klasie (private). Co to oznacza w praktyce? Ponieważ metoda getWaga() oznaczona jest modyfikatorem 'public', możliwe jest w każdym momencie odwołanie do niej. Właściwość $waga z kolei w pierwotnej definicji była oznaczona jako 'private' (w obecnej jako dziedziczona składowa 'protected' klasy 'Kot'), więc odwołanie:

echo $Puszek->waga;

Powróćmy do naszego dziedziczenia. Stwórzmy teraz definicję klasy 'Lew'. Lew to też rodzaj Kota, więc spokojnie możemy założyć, że Lew DZIEDZICZY cechy Kota:

class Lew extends Kot {
	private $grzywa;

	public function __construct($wiek, $ubarwienie, $waga , $grzywa) {
		$this->wiek = $wiek;
		$this->ubarwienie = $ubarwienie;
		$this->waga = $waga;
		$this->grzywa = $grzywa;
	}//
}

Składowe statyczne, czyli wszystkie koty mają cztery łapy.

Czym jest statyczna składowa klasy? Załóżmy, że wszystkie obiekty (zwierzęta) klas dziedziczących po klasie 'Kot' mają po cztery łapy. Nie ma więc potrzeby inicjalizacji tej składowej dla każdego kota indywidualnie. Przydałaby się możliwość narzucenia wszystkim obiektom z góry takiej właśnie ilości łap. Można to osiągnąć dzięki składowym statycznym:

private static $lapy = 4;
private static $ile_lap = 4;

 public function dodajLape() {
	self::ile_lap++;
 }//

Użycie zmiennych statycznych nie sprowadza się jedynie do tego typu zastosowań. Jest to technika pozwalająca między innymi na realizację wzorca projektowego Singleton (pojedyncza instancja) czy też na tworzenie np. licznika obiektów (inkrementowanego o jeden np. w konstruktorze, przy każdym nowo utworzonym obiekcie danej klasy - odczytanie tej zmiennej z dowolnego obiektu zwróci ich aktualną liczbę).Oprócz właściwości statycznych statyczne mogą być również metody. Różnią się one od zwykłych metod tym, że można się do nich odwoływać BEZ utworzenia instancji obiektu danej klasy, np. w ten sposób:

class Tygrys {
	
 	public static function rycz() {
		echo "Jestem tygrys i ryczę...";
 	}//
}

Tygrys::rycz();		//w efekcie wyświetli się tekst "Jestem tygrys i ryczę..."
(...)

Zauważ, że w takiej sytuacji odwołanie się do tego rodzju metody polega na podaniu nazwy klasy implementującej ją, a następnie, po znaku :: nazwy metody. Nie utworzyliśmy również obiektu klasy Tygrys. Zagadnienie metod statycznych i Singletonów nieco wykracza poza ramy tego artykułu i nie będziemy się nim więcej zajmować.

Obiekty z obiektów, czyli czas zbudować to Zoo.

class Wybieg {
	private $powierzchnia;
	private $czy_jest_zbiornik_wodny;
	
	private $Animal;    // obiekt klasy 'Animal'

	public function __construct(Animal $Animal , $powierzchnia, $czy_jest_zbiornik_wody) {
		$this->Animal = $Animal;
		...reszta implementacji...
	}//

	public function spaceruj() {
		$this->Animal->spaceruj();
	}//
}
$tablica_kotow = array();	//zawsze inicjalizujemy zmienną, nawet wartością zerową 
$tablica_kotow['Puszek'] = new Tygrys(5, 'bordowy', 100);
$tablica_kotow['Okruszek'] = new Tygrys(7 , 'brązowo-czarny'  ,120);
$tablica_kotow['Klebuszek'] = new Tygrys(12 , 'siwy' , 80);

echo $tablica_kotow['Puszek']->getWaga();    //wyswietli wage Puszka, czyli pierwszego elementu
 					     //tablicy asocjacyjnej o kluczu 'Puszek', w tym przypadku 100

Zdefiniujmy sobie zatem klasę 'Zoo', której jedną z właściwości będzie tablica zawierająca w sobie obiekty reprezentujące wybiegi dla zwierząt:

class Zoo {
	private $wybiegi = array();
	private $godz_otw;
	private $godz_zam;

	public function __construct($otw , $zam , $wybiegi) {
		$this->godz_otw = $otw;
		$this->godz_zam = $zam;
		$this->wybiegi = $wybiegi;
	}//

	public function pokazZwierze ( $ktory_wybieg ) {
		$this->wybiegi[$ktory_wybieg]->spaceruj();
	}//

	public function zakwaterujZwierze ( $ktory_wybieg , Animal $animal) {
		$this->wybiegi[ $ktory_wybieg ]->zakwateruj( $animal );
	}//
}//

Teraz czas na definicję klasy 'Wybieg':

class Wybieg {
	private $powierzchnia;
	private $czy_jest_zbiornik_wodny;
	
	private $Animal;    // obiekt klasy 'Animal'

	public function __construct($powierzchnia, $czy_jest_zbiornik_wody) {
		$this->powierzchnia = $powierzchnia;
		$this->czy_jest_zbiornik_wodny = $czy_jest_zbiornik_wodny;
	}//

	public function spaceruj() {
		$this->Animal->spaceruj();
	}//

	public function zakwateruj(Animal $animal) {
		$this->Animal = $animal;
	}//
}

Pora wreszcie stworzyć nasze Zoo:

$wybiegi = array();

$Puszek = new Tygrys(10, "pasiasty" , 150);
$Leon = new Lew(8 , "brązowy" , 180 , "bardzo gęsta grzywa");

$wybiegi['wybieg_Puszka'] = new Wybieg (100 , false);
$wybiegi['wybieg_Leona'] = new Wybieg(120, true);

$Zoo = new Zoo(8,16,$wybiegi);
	
$Zoo->zakwateruj("wybieg_Puszka" , $Puszek;);
$Zoo->zakwateruj("wybieg_Leona" , $Leon);

//wchodza zwiedzajacy, czas zaprezentowac zwierzeta
$Zoo->pokaz_zwierze('wybieg_Leona');   //wychodzi Leon :)
$Zoo->pokaz_zwierze('wybieg_Puszka');   //wybiega Puszek... :)

Wracamy do początku, czyli aplikacja do zarządzania Zoo.

  • klasa abstrakcyjna 'Animal', w której możemy zaimplementować m.in. metody jedz() (bo wszystkie zwierzęta jedzą) oraz niektóre wspólne właściwości (nazwa gatunku,rodziny, imię zwierzęcia),to w niej implementujemy funkcje związane z zapisem informacji do bazy danych czy też aktualizacją np.godziny ostatniego karmienia. Z tej klasy wywodzimy (dziedziczymy po niej) klasy reprezentujące np. ssaki, ryby oraz ptaki, w nich implementując metody specyficzne dla danej rodziny (plywaj(), fruwaj() itp.). Dopiero z tych klas wywodzimy konkretne gatunki zwierząt (jak w naszym przykładzie: klasa 'Tygrys' czy 'Lew'). Te klasy posłużą do utworzenia konkretnych zwierząt - obiektów zamieszkujących nasze Zoo,
  • klasa abstrakcyjna 'Pomieszczenie', która posłuży do utworzenia klas definiujących rodzaje pomieszczeń zajmowanych przez zwierzęta (wybiegi, akwaria, zbiorniki wodne itp.) W nich możemy zaimplementować metody odpowiedzialne za informowanie pracowników o stanie pomieszczenia (czy wymaga sprzątania), czy też jako składową uczynic obiekt (obiekty) klasy 'Animal' reprezentującą zamieszkujące dane pomieszczenie zwierzę.
  • klasa 'Zoo', będącą tablicą obiektów 'Pomieszczenie'.
  • klasa 'Pracownik' (w której możemy zaimplementować np. metodę sprzatajPomieszczenie() czy też nakarmZwierze() - taka metoda jako parametr przyjmować powinna obiekt klasy 'Pomieszczenie' (jak wiemy, jej składową jest obiekt 'Animal' będący reprezentacją zamieszkującego je zwierzęcia)- dzięki temu możemy z poziomu tej metody nakazać pracownikowi nakarmienie odpowiedniego mieszkańca Zoo (odpowiednia metoda w klasie 'Animal', która w tym momencie zostanie wywołana, zadba o aktualizację tabeli w bazie danych zawierającej godziny karmienia zwierząt)
  • klasa 'Aplikacja', której zadaniem będzie utworzenie instancji wszystkich obiektów, zapełnienie nimi naszego Zoo, obsługą wywołań metod, stworzeniem i zarządzaniem połączeniem z bazą danych i pozostałymi funkcjami.

Podsumowanie.

Z powyższego artykułu dowiedziałeś się, że:

  • podstawą OOP jest obiekt, będący egzemplarzem (instancją) klasy, która definiuje jego składowe: cechy (właściwości) oraz działania, które obiekt może podejmować (metody)
  • klasy mogą być bazą dla innych klas, które dziedzicząc po nich pewne cechy i dodając swoje, pozwalają w prosty sposób budować obiekty o coraz większym poziomie szczegółowości (dziedziczenie)
  • niektóre elementy lepiej zabezpieczyć przed zmianą ich wartości bez użycia specjalizowanych metod do tego przeznaczonych (hermetyzacja)
  • obiekty mogą być budowane także z użyciem obiektów innych klas, pozwalając na rozbicie większego fragmentu aplikacji na kilka mniejszych, bardziej autonomicznych, lecz nie powiązanych ze sobą w żaden sposób, który umożliwiałby ich dziedziczenie (porównaj przykład obiektu 'Animal', będącego jedną ze składowych obiektu 'Pomieszczenie')
  • raz napisaną klasę możesz wykorzystać w dowolnej aplikacji, w dowolny sposób. Z wielu dobrze zaprojektowanych klas możesz zbudować swój własny, mały framework, na którym możesz opierać swoje projekty, nie martwiąc się podstawowymi kwestiami występującymi w każdej z nich (połączenia z bazą danych, obsługa sesjii, tablic superglobalnych itp.)

Co dalej ?

Wasze opinie
Wszystkie opinie użytkowników: (0)
Mentax.pl    NQ.pl- serwery z dodatkiem świętego spokoju...   
O nas | Kontakt | Mapa serwisu
Copyright (c) 2003-2024 php.pl    Wszystkie prawa zastrzeżone    Powered by eZ publish Content Management System eZ publish Content Management System