Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Roznica miedzy interfejsem i klasa abstrakcyjna
Forum PHP.pl > Forum > PHP > Object-oriented programming
Vomit
Witam,

Nigdzie nie moge doczytac jaka jest praktyczna roznica miedzy interfejsem i klasa abstrakcyjna. Teorie znam, interfejs definiuje nagłowki metod danej klasy, klasa abstrakcyjna ma wieksze mozliwosci, moze ponadto definiowac zawartosc metody.

Chodzi mi jednak jak to sie sprawdza w praktyce, kiedy zastosowac interfejs a kiedy klase abstrakcyjna, po co tak naprawde mam w ogole definiowac "ogolny zarys" klasy w interfejsie czy klsaie abstrakcyjnej?

Pozdrawiam
nasty
Interfejs sie najbardziej sprawdza kedy ty piszesz program i API do tego programu, wtedy zmuszasz uzerow do uzywania interfejsu, i wiesz jak np. wywolac ten mod.
TomASS
Dam Ci definicję i przykład. Poprę się tym samym ksiązką "php 5 Nowe Możliwości"

1. Interfejsy
Przy pomocy interfejsów masz możliwość wymuszenia na klasach, aby posiadały niezbędne do współpracy z innymi elementami metody. Np. piszesz sklep i kążdy produkt musi mieć nazwę, cenę i numer identyfikacyjny. Robisz sobie interfejs:
  1. <?php
  2. interface Produkt{
  3.  public function getName();
  4.  public function getPrice();
  5.  public function getID();
  6. }
  7. ?>

We wnętrzu interfejsu definiuje się prototypy metod.
teraz każda klasa, która będzie implementowała ten interfejs musi posiadać podane metody. Np. klasa Book:
  1. <?php
  2. class Book implements Produkt{
  3. public function getName(){...}
  4. public function getPrice(){...}
  5. public function getID(){...}
  6. }
  7. ?>

Jeśli klasa nie będzie posiadała wszystkich metod wymieniony w interfejscie php wygeneruje błąd krytyczny.


2. Klasy abstrakcyjne
Klasy te mają to do siebie, że nie można stworzyć ich kopii. Np. robisz sobie klasę Ziemia.
  1. <?php
  2. abstract class Ziemia{
  3. abstract public function Wirowanie();
  4. abstract public function Uderzenie_komety();
  5. }
  6. ?>

Nie mogą istnieć dwie Ziemie smile.gif
Vomit
TomASS, ale widze ze to sie sprawdza tylko wtedy kiedy dajemy skrypt do rozbudowy przez innego programiste, bo przeciez sam i tak wiem jakie metody ma zawierac dana klasa...
Ludvik
Programowanie to zazwyczaj praca grupowa, dlatego powstają takie, a nie inne rozwiązania. Równie dobrze, można pisać w assemblerze, bo przecież nikt inny tego czytać nie będzie.

Interfejsy i klasy abstrakcyjne pomagają w uporządkowaniu struktury aplikacji. Pozwalają na zachowanie kontroli typów, dzięki czemu możesz się spodziewać określonego zachowania po danym obiekcie.

Klasy abstrakcyjne pozwalają na zdefiniowanie niektórych metod, dzięki czemu tworząc rodzinę klas, nie musisz dla każdej implementować funkcji takich jak getId(). Sprzyja to powstawaniu mniejszej ilości błędów (eliminujemy powtórzenia kodu). Drugą właściwością tych klas jest fakt, że nie możesz utworzyć z nich obiektu. Przydaje to się do tworzenia całkowicie statycznych klas.
Prph
Nie koniecznie dla innego programisty. Piszac duza aplikacjie (tzn zlozona), w koncu zapomnisz o czyms. Interfejsy ratuja wtedy skore winksmiley.jpg

Klase definiuje sie jako abstrakcyjna w chwili, kiedy nie chcesz aby ktos utworzyl jej obiekt. Taka klase da sie tylko dziedziczyc, co w praktyce oznacza ze klasa abstrakcyjna nie jest kompletne klasa. Dziedziczenie uzupelnia klase i wtedy mozemy stworzyc dzialajacy obiekt. Pokaze na przykladzie:

Mamy w aplikacji klase wyswietlajaca dane (widok w MVC). Czesto piszemy kilka widokow - np. taki wyswietlajacy dane na podstawie szablonow php. Do tego doliczmy, ze mozemy miec widok wyswietlajacy dane jak leci oraz taki, ktory udekoruje je o cala strone (stopka, naglowek, menu itp.).

Wiadomo, ze takie widoki powinny miec metody taki, jak:

  1. <?php
  2.  
  3. interface IView
  4. {
  5. public function setAttribute($sAttributeName, $mAttributeValue);
  6. public function setAttributesArray($aAttributeArray);
  7.  
  8. public function getAttribute();
  9. public function removeAttribute($sAttributeName);
  10.  
  11. public function __get($sAttributeName);
  12. public function __set($sAttributeName, $mAttributeValue);
  13.  
  14. public function display();
  15. public function fetch();
  16. }
  17.  
  18. ?>


W ten sposob zdefiniowalismy interfejs dla klas widoku. Od tech chwili, kazda implementujaca klasa ten interfejs bedzie musiala je miec.

Ale pomyslmy przez chwile. Czy klasa widoku normalna i dekorujaca nie bedzie miala takich samych metod getAttribute(), removeAttribute(), __set(), __get()? Po co powielac kod - napiszemy klase abstrakcyjna:

  1. <?php
  2.  
  3. abstract class View implements IView
  4. {
  5. private $_aAttributes;
  6.  
  7. public function __construct()
  8. {
  9. $this->_aAttributes = array();
  10. }
  11.  
  12. public function setAttribute($sAttributeName, $mAttributeValue)
  13. {
  14. $this->_aAttributes[$sAttributeName] = $mAttributeValue;
  15. }
  16.  
  17. public function setAttributesArray($aAttributeArray)
  18. {
  19. $this->_aAttributes = array_merge($this->_aAttributes, $aAttributeArray);
  20. }
  21.  
  22. public function getAttribute()
  23. {
  24. if(func_num_args() == 0)
  25. return null;
  26. $aArgs = func_get_args();
  27.  
  28. $aCurrentAttribute = $this->_aAttributes;
  29.  
  30. foreach($aArgs as $sKey => $sAttributeName)
  31. {
  32. if(isset($aCurrentAttribute[$sAttributeName]))
  33. $aCurrentAttribute = $aCurrentAttribute[$sAttributeName];
  34. else
  35. return null;
  36. }
  37.  
  38. return $aCurrentAttribute;
  39. }
  40.  
  41. public function removeAttribute($sAttributeName)
  42. {
  43. if(isset($this->_aAttributes[$sAttributeName]))
  44. unset($this->_aAttributes[$sAttributeName]);
  45. }
  46.  
  47. public function __get($sAttributeName)
  48. {
  49. $aAttributes = explode(_LANGUAGE_SEPARATOR, $sAttributeName);
  50. return call_user_func_array(array($this, 'getAttribute'), $aAttributes);
  51. }
  52.  
  53. public function __set($sAttributeName, $mAttributeValue)
  54. {
  55. $this->setAttribute($sAttributeName, $mAttributeValue);
  56. }
  57.  
  58. public function display()
  59. {
  60. echo $this->fetch();
  61. }
  62. }
  63.  
  64. ?>


Dlaczego klasa musi byc abstrakcyjna:
1. Pominmy slowo kluczowe abstract. Proba utworzenia obiektu tej klasy zakonczy sie bledem. Klasa przeciez nie definiuje metody fetch(), ktora wymusza interfejs.

2. Nie chcemy przeciez utowrzyc (najczesciej przez pomylke) obiektu widoku, ktory nie dziala. Lepiej rozszerzac klase (dziedziczyc od niej). W ten sposob zbudujemy klasy widokow, ktore dzialaja, a maja mniej kodu (bo przeciez dziedzicza z tej klasy abstrakcyjnej).

A jak wyglada przykladowy widok?

  1. <?php
  2.  
  3. class BasicHtmlView extends View
  4. {
  5. private $_sTemplate;
  6.  
  7. public function fetch()
  8. {
  9. if(!isset($this->_sTemplate))
  10. throw new ViewException('The template has not been specyfied');
  11.  
  12. $sTemplateFileName = _DIR_APPLICATION_TEMPLATES . $this->_sTemplate . 'Action.php';
  13.  
  14. if(!is_readable($sTemplateFileName))
  15. throw new ViewException('The template ' . $this->_sTemplate . ' does not exist');
  16.  
  17.  
  18. require_once($sTemplateFileName);
  19.  
  20. $sFetched = ob_get_contents();
  21.  
  22. return $sFetched;
  23. }
  24.  
  25. public function setTemplate($sTemplateName)
  26. {
  27. $this->_sTemplate = $sTemplateName;
  28. }
  29. }
  30.  
  31. ?>


Dodam jeszcze, ze nowy widok nie musi juz implementowac interfejsu, poniewaz dziedziczy implementacje z klasy abstrakcyjnej,

Proste i wygodne smile.gif

Pozdrawiam, Adrian.
splatch
Interfejsy uwalniają nas w pełni od jakiejkolwiek implementacji.
Klasy abstrakcyjne zawierają już implementację niektórych mechanizmów, które zachowują się w ten sam sposób w wielu miejscach. Możesz określić wewnątrz klasy abstrakcyjnej wszystkie metody jako finalne i jedną jako abstrakcyjną, którą powinna implementować tylko klasa wyspecjalizowana. smile.gif

Są ludzie, którzy twierdzą, że zanim napiszesz jakąkolwiek klasę powinieneś stworzyć interfejs. Trudno się z nimi niezgodzić.. sam powoli zaczynam stosować tą praktykę i muszę przyznać, że jest ona bardzo użyeczna. smile.gif
Strzałek
TamASS ładnie wytłumaczył.
Warto dodać jeszcze jedną dość ważną rzecz.
Otóż w php można dzidziczyć jedynie po jednej klasie, natomiat interfaców, można implementować wiele smile.gif

Więc kontynując post TomASS'a, w kodzie mozemy stworzyć:

  1. <?php
  2. class CookBooks implements IBook, IProduct {
  3. // ....
  4. }
  5. ?>


W naszym sklepie mamy książkę kucharską, która jest produktem - ksiązką smile.gif Więc musi posiadać metody interfejsu produktu oraz książki.

W telegraficznym skrócie.
Bez odbioru.
bela
Cytat(TomASS @ 16.07.2006, 10:50 ) *
2. Klasy abstrakcyjne
Klasy te mają to do siebie, że nie można stworzyć ich kopii. Np. robisz sobie klasę Ziemia.
  1. <?php
  2. abstract class Ziemia{
  3. abstract public function Wirowanie();
  4. abstract public function Uderzenie_komety();
  5. }
  6. ?>

Nie mogą istnieć dwie Ziemie smile.gif


Nie kopii, a obiektów. Trzeba je wydziedziczyć i nadpisać wszystkie metody abstrakcyjne.
Dwa obiekty mogą być typu Ziemia. Co prawda pośrednio, bo musisz wydziedziczyć Ziemia, ale zawsze.
TomASS
Cytat
Nie kopii, a obiektów.

Kopii tongue.gif

Cytat
Dwa obiekty mogą być typu Ziemia. Co prawda pośrednio, bo musisz wydziedziczyć Ziemia, ale zawsze.

Zależy jak na to spojżeć tongue.gif Wg. mnie nie możesz zrobić czegoś takiego:

  1. <?php
  2. $ziemia1 = new Ziemia();
  3. $ziemia2 = new Ziemia();
  4. ?>


Oczywiście masz rację, jeśli chodzi o "pośredniość" i "wydziedziczenie".
bela
Cytat(TomASS @ 16.07.2006, 21:31 ) *
  1. <?php
  2. $ziemia1 = new Ziemia();
  3. $ziemia2 = new Ziemia();
  4. ?>

Oczywiście, że nie można bo jak napisałeś, Ziemia to klasa abstrakcyjna, a nie dlatego, że może posiadać tylko jedną instancję.
TomASS
Skoro tak mówisz smile.gif Poddaję się i przyznaję, że masz rację smile.gif
Strzałek
Cytat
Skoro tak mówisz smilingsmiley.gif Poddaję się i przyznaję, że masz rację smilingsmiley.gif


Dobrze mówi ponieważ nie można tworzyć instancji klasy abstrakcyjnej.
Poprostu klasą abstrakcyjną w tym przypadku powinno być Planet, a ziemia powinna odziedzić planet czyli EarthPlanet() etc.
envp
Hm, roznica wydaje sie byc nieznaczaca, ale jednak jest. W intrefejsie podajemy tylko nazwy metod, a w klasie abstrakcyjnej mozemy w takiej metodzie zawrzec jakies dzialanie. Po co? - odpowiedz jest prosta jesli nasza metoda (w interfejsie) ma 10 linijek kodu, interfejsu uzywamy w 10 klasach, a metoda dla niech jest taka sama, to po co pisac 10x10 linijek kodu wiecej, skoro mozna zrobic abstrakcyjna klase z metoda juz zdefiniowana i pozostale klasy zrobic jej dziecmi?
athabus
Mi wydaję się, że różnica jest bardzo duża smile.gif Ostatnio np. pisałem klasę która miała około 20 metod, w tym chyba 2 abstrakcyjne -> klasy dziedziczące nadpisywały tylko te dwie metody. Tutaj klasa abstrakcyja była bardzo fajnym rozwiązaniem.

Interfejs ma natomiast inną przewagę: jedna klasa może implementować bardzo wiele interfejsów.
Druga kwestia, taka może troche bardziej koncepcyjna, to to, że klasa abstrakcyjna zazwyczaj jest dość mocno związana z obiektami dziedziczącymi w sensie logicznym -> czyli np. jak ktoś mówił tworzysz klasę Planet a potem wszystkie planety dziedziczą po tej klasie.
Interfejsc natomiast nie musi być już tak mocno związany z daną klasą -> np możesz stoworzyć interfejs Idestroyable, który mówi że dany obiekt może zostać zniszczony. Taki interfejs możesz nadać zarówno obiektomy klasy Planet, Star, Person, Car itp.

Różnica IMHO jest dość znaczna, ale wychodzi dopiero w większych projektach, gdzie trzeba śledzić wiele zależności itp. W małych projektach typu strona z panelem admina różnica jest często praktycznie niewidoczna.
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.