Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Zastosowanie interfejsów w PHP
Forum PHP.pl > Forum > PHP > Object-oriented programming
Grzesiek
Ostatnio natknąłem się na artykuł, mówiący, że interfejsy w PHP są zbędne bo w skrócie ... nie działają jak w "Dżawie" (linka nie przytocze), więc podaje moje 2 zastosowania jakie znalazłem dla interfejsów w PHP. Odrazu zaznaczam, że mój post przeznaczony jest dla początkujących,

1. Interfejs warto zdefiniować jeżeli dajemy programiście możliwość zastąpienia jednej klasy inną zdefiniowaną przez programiste. Coż nie wiem jak to ładnie napisać żeby było fachowo, więc posłuże się przykładem.

W Zend Framework-u domyślny widok (Zend_View) można zastpić dowolnym systemem szablonów np. Smarty. Jednak żeby to było możliwe Smarty musi być kompatybilne z Zend_View, i tu wkracza Zend_View_Interface, wystarczy, że Smarty będzie go implementować i może być wykorzystane w ZF (oczywiście pozostaje jeszcze stworzenie tej implementacji ale to już inna historia), więcej na ten temat napisane jest w manualu Zend View.

2. Nie lubie przykładów oderwanych od rzeczywistości, ale nic lepszego nie wymyśliłem, a więc ... Załóżmy, że mamy klasę Zoo do której metodą addAnimal będziemy dodawać zwierzęta, jednocześnie chcemy kontrolować czy dodawane zwierze jest obiektem odpowiedniego typu, ale każdy gatunek jest reprezentowany inną klasą (małpa - Monkey, tygrys - Tiger itd). Z takiego problemu możemy wybrnąć stosując interfejs Animal, który będą implementować klasy reprezentujące gatunki zwierząt.
  1. <?php
  2. interface Animal {
  3. }
  4.  
  5. class Monkey implements Animal {
  6. }
  7.  
  8. class Tiger implements Animal {
  9. }
  10.  
  11. class Zoo {
  12.    public function addAnimal(Animal $animal) {
  13.        // dodanie do tablicy czy cos takiego
  14.    }
  15. }
  16. ?>


Zapraszam do dyskusji.
phpion
Moim zdaniem przykład nie jest do końca trafiony. Lepiej chyba by było w tym przypadku zastosować dziedziczenie niż implementację interfejsu. Według mnie lepszym przykładem może być np. mechanizm wysyłający informacje różnymi metodami (sms, e-mail):
  1. <?php
  2. interface Sendable {
  3.    public function getTo();
  4. }
  5.  
  6. class PhoneNumber implements Sendable {
  7.    public function getTo() {
  8.        return '609123456';
  9.    }
  10. }
  11.  
  12. class Email implements Sendable {
  13.    public function getTo() {
  14.        return 'address@example.com';
  15.    }
  16. }
  17.  
  18. class Sender {
  19.    public function addTo(Sendable $obj) {
  20.        // operacje
  21.    }
  22. }
  23. ?>

aczkolwiek nie wiem czy w dobrym stopniu prezentuje ideę interfejsów. Ogólnie chodzi o to, że implementacje interfejsu wymusza posiadanie przez klasę wszystkich metod definiowanych przez interfejs. W przypadku podanym przeze mnie jest to metoda getTo(). Nieważne co się dzieje w poszczególnych klasach - ważne, że jest możliwość zwrócenia przez każdą z nich ciągu reprezentującego odbiorcę wiadomości.
Grzesiek
Rzeczywiście Twój przykład wydaje sie być o niebo lepszy, mój cóż posłuży za przykład kiedy nie stosować interfejsów tongue.gif chociaż nic nie stoi na przeszkodzie zeby jednocześnie skorzystać z dziedziczenia i interfejsu z drugiej strony mając klase abstrakcyjna ... aaa mniejsza o to smile.gif
bartg
Osobiście z interfejsów korzystam gdy piszę klasę z adapterami. Wtedy jak zapomnisz o jakiejś metodzie itp. PHP wywali błąd.

  1. <?php
  2. class Database implements DatabaseInter
  3. {
  4. public function connect($adapter) {
  5. $adapter->connect();
  6. }
  7. }
  8. class Sqlite implements DatabaseInter
  9. {
  10. public function __construct($sFile)
  11. {
  12. //tu ustawiasz confingi
  13. }
  14. public function connect()
  15. {
  16. //wczytanie pliku itd
  17. }
  18. }
  19. interface DatabaseInter {
  20. public function connect();
  21. }
  22. $db = new Database(new Sqlite('db.sqlite'));
  23. ?>
wrzasq
Interfejsy w PHP są nie tyle zbędne, co tak jak reszta obiektówki w nim - po prostu kulawe, albo przestrzelone (wystarczy chwila namysłu, przecież wszystko co w PHP nam przeszkadza zazwyczaj jest spowodowane tym, że główna idea PHP jest taka, aby był on dynamiczny, a tutaj interfejsy, które na nas coś wymuszają...). Poza tym brak w PHP możliwości type-hintingu dla zwracanych wartości funkcji dodatkowo przeszkadza w zwiększaniu użyteczności interfejsów. Ale oczywiście tak jak tutaj wszyscy już napisali są momenty kiedy się przydają.

Najczęściej oczywiście w przypadku, gdy udostępniamy nasz kod na zewnątrz, dajemy innym możliwość korzystania z niego, albo wręcz jego modyfikacji/rozbudowy.

Co do użycia to trzeba pamiętać, że interfejs to twór czysto abstrakcyjny. Określa jedynie sposoby wykorzystania bez żadnej namiastki implementacji. Dlatego tak jak phpion powiedział, w przypadku Animal -> Monkey bardziej naturalne (i imho poprawne) byłoby użycie dziedziczenia - zwierzę to już jakiś (jak bardzo ogólny to nie ważne, ale jednak) byt, który nam od razu przywodzi na myśl jakieś zyjące stworzenie z kończynami i tak dalej...

Skoro już tak z tymi zwierzętami to ja bym zaproponował inną nieco strategię podziału, żeby zobrazować interfejsy:

  1. <?php
  2.  
  3. interface Flying
  4. {
  5.    public function start();
  6.    public function fly();
  7.    public function land();
  8. }
  9.  
  10. abstract class Animal
  11. {
  12. }
  13.  
  14. class Bird extends Animal implements Flying
  15. {
  16.    public function start()
  17.    {/* ... */}
  18.    public function fly()
  19.    {/* ... */}
  20.    public function land()
  21.    {/* ... */}
  22. }
  23.  
  24. class Monkey extends Animal
  25. {
  26. }
  27.  
  28. class AirPlane
  29. {
  30.    public function start()
  31.    {/* ... */}
  32.    public function fly()
  33.    {/* ... */}
  34.    public function land()
  35.    {/* ... */}
  36. }
  37.  
  38. function moveObjectByAir(Flying $object)
  39. {/* ... */}
  40.  
  41. moveObjectByAir( new Bird() ); // zadziala
  42. moveObjectByAir( new AirPlane() ); // zadziala
  43. moveObjectByAir( new Monkey() ); // nie zadziala
  44.  
  45. ?>


Z bardziej realnych i praktycznych przykładów: tworzę system wymiany ofert dla biur nieruchomości i metody wymiany ofert są przeróżne. System, żeby ułatwić integrację z już istniejącymi mechanizmami powinien obsługiwać obecne metody synchronizacji (serwisów w stylu Gratka.pl, OtoDom.pl i tak dalej). I jedne eksporty opierają na plikach XML, inne na API w SOAP'ie, jeszcze inne na plikach we własnym INI-podobnym formacie. A jednak wszystkie "sterowniki" eksportu muszą udostępniać jednakowy interfejs. Poglądowo mniej więcej:

  1. <?php
  2.  
  3. interface ExportInterface
  4. {
  5.    public function init();
  6.    public function sync();
  7.    public function full();
  8. }
  9.  
  10. class GratkaExport implements ExportInterface
  11. {
  12. /* export bazujący na plikach XML */
  13. }
  14.  
  15. class OtoDomExport extends SoapClient implements ExportInterface
  16. {
  17. /* export z wykorzystaniem SOAP'u */
  18. }
  19.  
  20. function exportOffers(ExportInterface $export, $full = false)
  21. {
  22.    $export->init();
  23.  
  24.    if($full)
  25.    {
  26.        $export->full();
  27.    }
  28.    else
  29.    {
  30.        $export->synch();
  31.    }
  32. }
  33.  
  34. ?>


(tutaj co najważniejsze chciałem pokazać, że Intefejsy pozwalają nam na użycie typowania instancji (type-hinting, instanceof i tak dalej) mimo dziedziczenia - w PHP nie ma dziedziczenia wielobazowego, ale interfejsów możemy implementować do woli, dlatego czasem są takie przydatne)
WaterIntelligence
Ja używanie interfejsów postrzegam przez pojęcie Roli jaką pełni klasa/interfejs w aplikacji. To bardzo przydatna strukturka, która uzmysławia nam co dzieje się w aplikacji w bardzo ogólny sposób. Jeśli dobrze zdefiniujemy interfejsy inni programiści łatwiej dojdą do tego jaka jest struktura ogólna aplikacji, jaki jest sposób komunikacji poszczególnych klas czy nawet klas zgrupowanych w moduły. Jest to odzwierciedlenie Komunikacji między klasami. A to że nazywamy to interfejsem również jest nie bez znaczenia - interfejs jako dostęp do czegoś innego, u nas - dostęp do klas. Interfejsem mogłaby być klasa z pustymi metodami, ale nazwyając rzeczy po imieniu wiemy odrazu o co chodzi.

Interfejs niczego nie wymusza, on pomaga - jeśli nie chcesz to go nie stosujesz, jeśli wiesz jak zastosować to stosujesz winksmiley.jpg .

Ja z reguły zaczynam pisanie aplikacji właśnie od interfejsów, oczywiście mówię o większych aplikacjach w których liczba klas przekracza np. 30 klas. W takiej aplikacji zależności między klasami potrafią być już dosyć zagmatwane i definiowanie interfejsów porządkuje całość zarówno wizualnie jak i projektowo. Dodatkowo zawsze staram się projektować tak aby rozszerzanie aplikacji polegało na dodawaniu nowych klas a nie na potrzebie modyfikacji już istniejących, z takim podejściem idea interfejsów sprawdza się znakomicie. Zdefiniowany interfejs jednoznacznie określa co musimy zrobić aby rozszerzyć funkcje jakiegoś modułu/klasy który/która komunikuje się z innymi. Przykład który podał phpion właśnie odzwierciedla tą ideę. Moglibyśmy dodać do niego jeszcze klasę Post{} która byłaby "reprezentacją" poczty - bytu który istnieje fizycznie.

Idea interfejsów ma wiele zagadnień projektowych. Przypomnę tylko, że czasami trzeba rozpatrzeć to z jakiego punktu patrzymy na to co się dzieje w aplikacji i wtedy zastosować interfejs do odseparowania klas i utworzenia kanału komunikacyjnego za pomocą interfejsów.
Maciekbjw
Interfejsy z powodzeniem stosuję we wzorcach projektowych, np Decorator. Można się nauczyć po co stosować i jak, jednak troszkę trzeba kodu popisać tongue.gif
Bart77
Cytat(wrzasq @ 21.01.2009, 01:57:19 ) *
strategię podziału, żeby zobrazować interfejsy:

  1. <?php
  2. // (...)
  3.  
  4. class AirPlane
  5. {
  6.    public function start()
  7.    {/* ... */}
  8.    public function fly()
  9.    {/* ... */}
  10.    public function land()
  11.    {/* ... */}
  12. }
  13.  
  14. function moveObjectByAir(Flying $object)
  15. {/* ... */}
  16.  
  17. moveObjectByAir( new Bird() ); // zadziala
  18. moveObjectByAir( new AirPlane() ); // zadziala
  19. moveObjectByAir( new Monkey() ); // nie zadziala
  20.  
  21. ?>


Chyba powinno być class AirPlane implements Flying

Głupio że pierwszy mój post na forum to czepianie się winksmiley.jpg Dzień dobry :]
korkie
Witam!

W PHP rzeczywiście interfejsy nie przypominają tych z Javy, czy rozwiązań z innych języków (np. mixiny w Ruby on Rails).
Tutaj interfejs oznacza dodatkowe warunki do jakich musi zastosować się klasa.
Klasycznym przykładem jest tzw. wzorzec obserwatora oparty na tzw. singletonie.
Klasa, która chce użyć singletona musi implementować interfejs z prototypem funkcji, a sama w sobie musi tej funkcji nadać "ciało".

Oto przykład Andiego Gutmansa - jednego z ojców php ( warto go przerobić i umieć jak pacierz ):

<?php

interface Observer {
function notify($obj);
}

class ExchangeRate {
static private $instance = NULL; # Zmienna singletona
private $observers = array(); # Tablica rejestrująca klasy implementujące interfejs
private $exchange_rate; #zmienna do funkcji function setExchangeRate($new_rate)

private function ExchangeRate() {
}

static public function getInstance() { # Singleton
if (self::$instance == NULL) {
self::$instance = new ExchangeRate();
}
return self::$instance;
}

public function getExchangeRate() {
return $this->$exchange_rate;
}

public function setExchangeRate($new_rate) {
$this->$exchange_rate = $new_rate;
$this->notifyObservers();
}

public function registerObserver($obj) {
$this->observers[] = $obj;
}

function notifyObservers() {
foreach($this->observers as $obj) {
$obj->notify($this);
}
}
}


class ProductItem implements Observer {
public function __construct() {
ExchangeRate::getInstance()->registerObserver($this);
}

public function notify($obj) {
if ($obj instanceof ExchangeRate) {
// Uaktualnij dane dotyczące kursu walut
print "Otrzymano aktualizację!\n";
}
}
}

$productl = new ProductItem();
$product2 = new ProductItem();

ExchangeRate::getInstance()->setExchangeRate(4.5);

?>

Wszystkie klasy implementujące interfejs i zarejestrowane poprzez ExchangeRate::getInstance()->registerObserver($this);
muszą mieć zdefiniowaną funkcję notify();
Dzięki temu będą instancje tych klas powiadamiane o zmianach wprowadzonych poprzez funkcję: function setExchangeRate($new_rate).

Najpopularniejsze wykorzystanie powyższego to informowanie w sklepie internetowym obiektów (tych, które powinny tą informację otrzymać)
o zajściu zmian np. kursu waluty.
Dzięki temu zostaną automatycznie wszędzie przeliczone ceny.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.