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

Dlaczego PHP5?

Nowy model obiektowy

Dla wszystkich zwolenników obiektowego podejścia do konstruowania oprogramowania mamy bardzo dobrą wiadomość: PHP5 to w pełni dojrzały, zorientowany obiektowo język programowania. Wiele konstrukcji znanych z Java i C++ jest nareszcie dostępnych w naszym ulubionym narzędziu. Co więcej, nowe elementy składni nie wymuszają całkowitej rezygnacji z podejścia strukturalnego, ale raczej uzupełniają język o potrzebne i od dawna wyczekiwane możliwości.

Na Listingu 4 znajdują się klasy, którymi będziemy posługiwać się w kolejnych przykładach, stopniowo rozbudowując definicje pokazanych klas o nowe możliwości zawarte w PHP5. Są to proste klasy modelujące dowolne zwierzę i drapieżnika. Nasze zwierzątka mogą być małe lub trochę większe, natomiast drapieżniki - jak to leży w ich naturze - potrafią kąsać. Dla mniejszych zwierzaczków takie ukąszenie może być niestety śmiertelne.

Okazuje się jednak, że takie brutalne zachowanie może być poprawnie modelowane tylko w PHP5. Powodem jest sposób przekazywania parametrów do metod, a dokładniej: parametrów będących obiektami. W PHP4 podczas wołania metody wartość argumentu jest kopiowana, a następnie ta powielona wartość jest przekazywana do ciała metody. Jeśli rozważymy składową bite($animal) i przykładowy fragment kodu z Listingu 5, to wynik jego wykonania będzie zupełnie inny w PHP4 i PHP5. W przypadku poprzedniej wersji języka zwierzę $a1 w przedziwny sposób zmartwychwstanie po wykonaniu metody bite($animal)! O ile funkcjonalność taka może ucieszyć miłośników zwierząt, to nie jest to zachowanie intuicyjne, znane z realnego świata, który próbujemy modelować. Dlatego w PHP5 do metody nie są przekazywane kopie obiektów, ale referencje do nich: przekazujemy tylko wskazanie, logiczne połączenie do jednej kopii obiektu. Takie zachowanie języka jest bardziej intuicyjne i znane z innych środowisk, np. Java.

Przekazywanie obiektów przez referencje nie oznacza, że nie możemy wykonać kopii obiektu - zmieniła się tylko składnia. Teraz kopie obiektu uzyskamy dzięki użycia słowa kluczowego clone, np. $a2 = clone $a1;.

Na Listingu 4 wykorzystaliśmy również inną nowość z repertuaru konstrukcji obiektowych PHP5: sterowanie zakresem widoczności pól. Zauważmy, że pola w klasie Animal są poprzedzone słowem kluczowym private. Jak w innych językach, oznacza to, że dostęp do tej składowej ma tylko kod umieszczony bezpośrednio w danej klasie. Jest to doskonały sposób na zakomunikowanie intencji projektanta klasy, który w ten sposób może powiedzieć: "To są wewnętrzne szczegóły obiektu, proszę ich nie zmieniać". Z drugiej strony, dzięki zmiennym prywatnym możemy chronić naszą klasę przed przypadkowym, nieprawidłowym użyciem. Nie chcielibyśmy przecież, by ktoś przypadkowo wskrzeszał obiekty klasy Animal, wykonując kod $a1->_isAlive = true;. W PHP4 jedynym sposobem ochrony zmiennych prywatnych było rozpoczynanie ich nazw od znaku podkreślenia i... liczenie na dobrą wolę użytkowników klasy.

Zmienne prywatne to nie jedyne modyfikatory widoczności pola. Do dyspozycji pozostają jeszcze: protected i public. Domyślnie, jeśli nie nadamy żadnego modyfikatora, przyjmowany jest dostęp na poziomie public, czyli bez ograniczeń, dla wszystkich klas. Dobrą praktyką jest jednak dopisywanie słowa kluczowego public, aby w ten sposób lepiej przekazać intencje co do przeznaczenia danej składowej.

Listing 4 jest też dobrą okazją do zademonstrowania innej cechy PHP5: nazewnictwa konstruktorów. W PHP4 jako konstruktor uruchamiana była metoda o nazwie identycznej z nazwą klasy.

Listing 4. Przykładowe klasy, które posłużą nam do omówienia nowych cech obiektowych PHP5

class Animal {
    private $_isSmall;
    private $_isAlive;
    public function __construct($isSmall, $isAlive = true){
        $this->_isSmall = $isSmall;
        $this->_isAlive = $isAlive;
    }
    public function isSmall(){
        return $this->_isSmall;
    }
    public function isAlive(){
        return $this->_isAlive;
    }
    public function kill(){
        $this->_isAlive = false;
    }
}
class Predator extends Animal {
    public function bite($animal){
        if ($animal->isSmall()) {
            $animal->kill();
        }
    }
}

Listing 5. Przekazywanie obiektów przez referencje

$a1 = new Animal(true, true);
$p1 = new Predator(true, false);
$a1->_isAlive = true;
$p1->bite($a1);
echo $a2->isAlive();

Listing 6. Użycie modyfikatora abstract

abstract class Animal {
    private $_isSmall;
    private $_isAlive;
    public function __construct($isSmall, $isAlive = true){
        $this->_isSmall = $isSmall;
        $this->_isAlive = $isAlive;
    }
    public abstract function move();
    public function isSmall(){
        return $this->_isSmall;
    }
    public function isAlive(){
        return $this->_isAlive;
    }
    public final function kill(){
        $this->_isAlive = false;
    }
}
class Rabbit extends Animal {
    public function move(){
        //skacz
    }
}
abstract class Predator
extends Animal {
    public function bite($animal){
        if ($animal->isSmall()) {
            $animal->kill();
        }
    }
}
class Tiger extends Predator {
    public function move(){
        //szybko biegaj
    }
}
$r1 = new Rabbit(true, true);
$t1 = new Tiger(false, true);
$t1->bite($r1);
echo $r1->isAlive();

Było to o tyle problematyczne, że zmieniając nazwę klasy musieliśmy dodatkowo modyfikować nazwę konstruktora. W PHP5 problem ten został rozwiązany: konstruktorem jest zawsze metoda o nazwie __construct(). Dodatkowo, pojawiła się specjalna metoda, wywoływana automatycznie przy usuwaniu obiektu z pamięci, czyli destruktor. Jak się łatwo domyślić, ma on nazwę __destruct().

Opisane powyżej zmiany w składni języka rozwiązują najbardziej dokuczliwe problemy związane z programowaniem obiektowym w PHP.

Na szczęście zmiany w PHP5 idą jeszcze dalej i dzięki temu dostajemy bardzo silne narzędzia w postaci interfejsów, klas abstrakcyjnych, finalnych i wiele, wiele innych. Przyjrzyjmy się dokładniej, co oznaczają te nowe możliwości, ulepszając klasy z Listingu 4. Przede wszystkim zauważmy, że metoda kill() jest kluczowa dla obiektów klasy Animal. Nie chcemy, by istniała możliwość zmiany jej definicji, a tym samym - podstawowego zachowania obiektu. W PHP5 istnieje sposób na wyrażenie takiej intencji. Słowo kluczowe final powoduje, że poprzedzona nim metoda nie może być przedefiniowana w klasie rozszerzającej. Poprawmy więc naszą definicję, zapisując ją jako: public final function kill(). Oprócz metod, również klasy mogą być oznaczone jako finalne, co uniemożliwia ich rozszerzenie przez jakąkolwiek inną klasę.

Bliższe przyjrzenie się naszemu modelowi obiektowemu może nasunąć pewną myśl: nasze klasy są bardzo ogólne. W rzeczywistym świecie mamy do czynienia z realnymi zwierzętami, żyjące obiekty są kotami czy królikami, nie zaś abstrakcyjnymi zwierzętami. W rozważanym przypadku klasa Animal jest pojęciem ogólnym, abstrakcyjnym, dla której nie powinny istnieć instancje obiektu. Również taki model możemy bardzo dobrze wyrazić w PHP5, stosując modyfikator abstract. Spójrzmy na Listing 6, gdzie znajdują się przykładowe klasy po zmianach. Widać teraz, jak łatwo jest zakomunikować istnienie abstrakcyjnej metody move(): w przypadku ogólnego pojęcia "zwierzę" konkretna implementacja nie ma sensu, ponieważ poszczególne stworzenia są inne np. poruszają się inaczej. Dlatego poprzedzamy tę nową metodę słówkiem abstract.

Obserwując dalej poprawiony kod możemy zauważyć, że w klasie Predator i metodzie bite nie ma mechanizmów sprawdzających, co tak naprawdę jest nadgryzane. Raczej mało prawdopodobne jest, by hipotetyczny tygrys z równą ochotą konsumował króliki i siano. W PHP, jako języku bez ścisłego określenia typów, możliwe jest przekazanie dowolnego argumentu do metody. Nawet takiego, na którym wołanie metod isSmall czy kill nie ma sensu. W PHP5 dodano dwa sposoby na zapobieganie takim niespodziewanym sytuacjom. Po pierwsze, możemy użyć nowego operatora instanceof. Dużo wygodniejsze jest jednak ustalenie typu argumentów, jakie może przyjmować dana metoda. Przykłady obu konstrukcji znajdują się na Listingu 7. Widać, że drugie z zaproponowanych rozwiązań przesuwa ciężar odpowiedzialności za sprawdzanie poprawności argumentów, z programisty na interpreter języka.

Przy podawaniu typu dla argumentów funkcji możemy używać zarówno klas zwykłych i abstrakcyjnych. PHP5 wprowadza jeszcze jedno pojęcie ściśle związane z typami danych: interfejsy. Najłatwiej myśleć o tych nowych konstrukcjach jako o w pełni abstrakcyjnych klasach (tzn. takich, w których wszystkie metody są abstrakcyjne), w których nie zdefiniowano pól z danymi. Na pierwszy rzut oka może wydawać się, że takie abstrakcyjne twory nie są zbyt użyteczne: nie zawierają danych i funkcjonalności. Okazuje się, że samo zdefiniowanie możliwych zachowań w interfejsie jest szalenie pożyteczne, bowiem interfejsów możemy używać jako określenia typów w parametrach funkcji, dokładnie w taki sam sposób, jak używaliśmy nazw klas. Listing 8 pokazuje definicję przykładowych interfejsów oraz jeszcze inną miłą ich cechę: klasy mogą implementować wiele interfejsów.

Ostatnią nowością PHP5 pozostałą do omówienia są składowe statyczne. W PHP4 możliwe było poprzedzenie zmiennej słowem static, ale tylko wewnątrz funkcji. PHP5 nie narzuca już tych ograniczeń i możemy cieszyć się pełną obsługą składowych statycznych. Co więcej, istnieje inna, bardzo podobna konstrukcja - stałych klasowych. Można myśleć o niej jak o polu statycznym, z tą jednak różnicą, iż nie można zmienić wartości raz przypisanej do stałej.

Przy okazji omawiania cech obiektowych języka, warto wspomnieć o nowym zestawie klas, standardowo dostępnym w PHP5. Klasy te zgromadzono w module ReflectionAPI, a służą one do pobierania informacji (dostępne metody i ich argumenty, konstruktor itd.) o zdefiniowanych klasach i w tym sensie zastępują takie funkcje jak get_declared_classes czy get_class_methods.

Listing 7. Operator instanceof i określanie typu w argumentach funkcji

public function bite($animal){
    if ($animal instanceof Animal) {
        if ($animal->isSmall()) {
            $animal->kill();
        }
    } else {
        hrow new
        InvalidArgumentException();
    }
}
public function bite(Animal $animal){
    if ($animal->isSmall()) {
        $animal->kill();
    }
}

Listing 8. Przykład użycia interfejsów

interface Moveable {
    public function move();
}
interface Killable {
    public function kill();
}
abstract class Animal
implements Moveable, Killable {
    private $_isSmall;
    private $_isAlive;
    public function __construct($isSmall, $isAlive = true){
        $this->_isSmall = $isSmall;
        $this->_isAlive = $isAlive;
    }
    public function isSmall(){
        return $this->_isSmall;
    }
    public function isAlive(){
        return $this->_isAlive;
    }
    public final function kill(){
        $this->_isAlive = false;
    }
}
Informacje na podobny temat:
Wasze opinie
Wszystkie opinie użytkowników: (5)
Brakujące listingi
Czwartek 11 Grudzień 2008 8:58:45 am - feft

Widzę, że do dzisiaj nie pojawiły się brakujące listingi - wielka szkoda.
Piotr

listingi
Wtorek 07 Sierpień 2007 8:14:16 am - Olimpia_ona

"Na Listingu 16 zaprezentowaliśmy prosty plik XML, którym będziemy manipulować w kolejnych przykładach."
A gdzie ten listing? Nie ma też listingu 17.
XML / WebServices to interesujący mnie temat, szkoda że z tego artykułu nic się nie dowiedziałam.

super
Czwartek 21 Czerwiec 2007 3:35:34 pm - pawelad <pawelkostomloty_at_op.pl>

Kocham obiektowosc a ostatnio interesuje sie php. Wasza stronka to miod na moje serce. No to macie kolejnego wyznawce.

magiczne metody, przestrzenie nazw
Sobota 11 Marzec 2006 11:38:11 am - mysz

Od wersji php5.1 doszły dwie magiczne metody: __isset i __unset, wywoływane, jak ich nazwa wskazuje, gdy na własności obiektu użyje się funkcji isset() lub unset(). Bardzo przyjemne.

A co do przestrzeni nazw, to mój sposób na implementacje ich w PHP5: http://diary.urzenia.net/?p=261

techniki obiektwe rulezzz
Sobota 28 Styczeń 2006 5:37:00 pm - emp

Ja sobie osbiście nie wyobrażam robić projekt w php bez udogodnien dla technik obiektowych... choć te narazie w wersji 5 wygladaja dość żałośnie to wole to od braku takich udogodnien.. pozatym łapanie wyjątków to podstawowe udogodnienie bez ktorego ciężko się obejśc w większych projektach... tak ja wiem ze takie powstały tylko przy użyciu php5 moze je wykonać jeden człowiek a nie zespół... czcionka jest qrefsko mała ledwo widze co pisze..

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