Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Zastosowanie interfejsów w PHP
Grzesiek
post
Post #1





Grupa: Zarejestrowani
Postów: 96
Pomógł: 3
Dołączył: 15.04.2003
Skąd: Kraków

Ostrzeżenie: (0%)
-----


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.


--------------------
Linux is like wigwam, no windows, no gates and an apache inside.
Mój blog łebmasterski (po angielsku) Web Development Blog.
Go to the top of the page
+Quote Post
phpion
post
Post #2





Grupa: Moderatorzy
Postów: 6 072
Pomógł: 861
Dołączył: 10.12.2003
Skąd: Dąbrowa Górnicza




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.

Ten post edytował phpion 14.01.2009, 18:40:06
Go to the top of the page
+Quote Post
Grzesiek
post
Post #3





Grupa: Zarejestrowani
Postów: 96
Pomógł: 3
Dołączył: 15.04.2003
Skąd: Kraków

Ostrzeżenie: (0%)
-----


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


--------------------
Linux is like wigwam, no windows, no gates and an apache inside.
Mój blog łebmasterski (po angielsku) Web Development Blog.
Go to the top of the page
+Quote Post
bartg
post
Post #4





Grupa: Zarejestrowani
Postów: 226
Pomógł: 25
Dołączył: 4.07.2007
Skąd: Berlin

Ostrzeżenie: (0%)
-----


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. ?>


Ten post edytował bartg 20.01.2009, 20:47:56


--------------------
Go to the top of the page
+Quote Post
wrzasq
post
Post #5





Grupa: Zarejestrowani
Postów: 206
Pomógł: 18
Dołączył: 6.03.2006
Skąd: Szczecin

Ostrzeżenie: (0%)
-----


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)

Ten post edytował wrzasq 21.01.2009, 01:58:43


--------------------
Go to the top of the page
+Quote Post
WaterIntelligenc...
post
Post #6





Grupa: Zarejestrowani
Postów: 7
Pomógł: 1
Dołączył: 29.09.2008
Skąd: Świdnica

Ostrzeżenie: (0%)
-----


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.


--------------------
Go to the top of the page
+Quote Post
Maciekbjw
post
Post #7





Grupa: Zarejestrowani
Postów: 217
Pomógł: 23
Dołączył: 2.12.2007
Skąd: Warszawa

Ostrzeżenie: (0%)
-----


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


--------------------
Masz swoje mieszkanie i chcesz je wynająć? Sprawdź ofertę Zarzadządzanie Najmem

WRONA.IT - pozycjonowanie stron
www.ecyklopedia.pl
Go to the top of the page
+Quote Post
Bart77
post
Post #8





Grupa: Zarejestrowani
Postów: 31
Pomógł: 3
Dołączył: 24.03.2009
Skąd: Poznań

Ostrzeżenie: (0%)
-----


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 :]
Go to the top of the page
+Quote Post
korkie
post
Post #9





Grupa: Zarejestrowani
Postów: 48
Pomógł: 9
Dołączył: 7.03.2009
Skąd: Łódź

Ostrzeżenie: (0%)
-----


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.

Ten post edytował korkie 30.04.2009, 19:34:06
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 22.08.2025 - 07:10