Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: klasy abstrakcyjne
Forum PHP.pl > Forum > PHP > Object-oriented programming
Jarod
Tworząc interfejs używamy słowa abstract przy definicji metod.

Po co używa się tego słowa przy definicji klas (lub funkcji - w przypadku gdy nie jest to interfejs) ?
W jakim celu tworzyć abstrackyjne klasy?
Apo
W przeciwieństwie do interfejsów w klasach abstrakcyjnych masz ciało metod, czyli częściowe zaimplementowanie interfejsu, możesz wymuszać obecność metod w klasach jak i iniemożliwić nadpisanie innej itp.

Fajnym przykładem może być obsługa akcji w frameworkach np:

  1. <?php
  2. abstract class SzablonAkcji {
  3.  
  4. protected $przywileje = array('public');
  5.  
  6. abstract public function akcjaAlternatywna(); // wymuszamy utworzenie tej metody w klasach dziedziczących
  7.  
  8. final function OdpalAkcje() // w klasach dziedziczących nie mozemy nadpisac tej metody
  9. {
  10. $action = new HttpRequest->get('action');
  11. $action = $this->$action . '()';
  12.  
  13. if(method_exists($action, $this))
  14. $this->action;
  15. else
  16. $this->akcjaAlternatywna();
  17. }
  18. }?>


Stworzony został prosty szkielet do obsługi akcji, czyli wywoływania odpowiednich metod na podstawie urla, aby wyświetlić przykładowo newsy musimy utworzyć klasa WyswietlNewsy która będzie dziedziczyć nasz SzablonAkcji.

  1. <?php
  2.  
  3. class WyswietlNewsy extends SzablonAkcji {
  4.  
  5. protected $przywileje('admin');
  6.  
  7. public function AkcjaAlternatywna()
  8. {
  9. echo 'Błąd obsługi akcji';
  10. }
  11.  
  12. public function WyswietlNewsy()
  13. {
  14. // wyswietlanie newsow
  15. }
  16.  
  17. }
  18. ?>


Pisałem z palca więc mogą byc błędy, ale myślę że teraz wiesz miej więcej jak to działa smile.gif
envp
Heh wytlumaczone dobrze, ale dziwnie. smile.gif J4r0d: pojęcie interfejsu znasz prawda? więc wyobraź sobie teraz ze masz 10 klas, ktore dziedzicza ten inerfejs (niech to bedzie metoda foo()) i dla tych klas metoda foo() wyglda tak samo, gdybys uzywal tylko interfesjow musail bys napisac 10 razy ten sam kod, a tak tworzysz sobie klase abstrakcyjna i ona zawiera juz cale cialo metody foo(). Dodatkowo powiem, ze nie mozesz utworzyc instacji ani odwolac sie do pola klasy abstrakcyjnej - czyli sama w sobie jest bezuzyteczna, uzywamy jej jedynnie jako rodzica innej klasy. Btw. Ten temat byl juz poruszany....
Ludvik
Klasy abstrakcyjne nie są wygodnym narzędziem do definiowania interfejsów. Sprawdzają się, kiedy musisz wymusić na potomkach określony zestaw metod, w którym część kodu metod jest wspólna. Ponadto przydają się przy takich wzorcach jak Template Method.

Cytat
Dodatkowo powiem, ze nie mozesz utworzyc instacji ani odwolac sie do pola klasy abstrakcyjnej - czyli sama w sobie jest bezuzyteczna, uzywamy jej jedynnie jako rodzica innej klasy.

To nie jest do końca prawdą. Statyczne metody i właściwości klasy abstrakcyjnej nie zmieniają swojego zachowania. Dobrym nawykiem jest zadeklarowanie klasy jako abstrakcyjnej w przypadku, gdy cały jej interfejs tworzą metody statyczne i nie ma ona sensu w kontekście instancji. Dla przykładu przywołać można szczególną postać wzorca Registry.

  1. <?php
  2. abstract class Registry {
  3. public static function get($mKey) {
  4. //...
  5. }
  6.  
  7. public static function set($mKey) {
  8.  //...
  9.  }
  10.  
  11. public static function unset($mKey) {
  12.  //...
  13.  }
  14.  
  15. public static function has($mKey) {
  16.  //...
  17.  }
  18.  
  19. private static $registry = array();
  20. }
  21. ?>
Jarod
Mniej więcej łapie. Wychodzi na to, że lepiej używać klas abstrakcyjnych niż interfejsów. Dziwni mnie tylko, że w książce PHP5 zaaw. progr. nie ma (przynajmniej we wprowadzeniu do OOP) wzmianki o czymś takim jak final czy klasy anstrakcyjne.. :/
Ludvik
Cytat
Wychodzi na to, że lepiej używać klas abstrakcyjnych niż interfejsów.

A skąd ten wniosek? To są narzędzia do dwóch podobnych, ale jednak różnych celów. Jeżeli chcesz zdefiniować tylko wymagany interfejs klasy, to używasz interfejsów. Jeżeli potrzebujesz utworzyć szkielet klasy, wtedy definiujesz klasę abstrakcyjną. Pamiętaj o jednej, znaczącej przewadze interfejsów. Możesz implementować wiele interfejsów w jednej klasie. Wielodziedziczenie w php nie istnieje.

W moich projektach na 10 (jeżeli nie więcej) interfejsów przypada mniej więcej jedna klasa abstrakcyjna.
Jarod
Cytat(Ludvik @ 10.08.2006, 18:29 ) *
Pamiętaj o jednej, znaczącej przewadze interfejsów. Możesz implementować wiele interfejsów w jednej klasie.


Przecież zapis nie ma prawa bytu
  1. <?php
  2. class NazwaKlas implements Interfejs1, Interfejs2
  3. {
  4.  
  5. }
  6. ?>
Ludvik
A sprawdziłeś czy to działa? Na mój gust działa... Tak się składa, że takie konstrukcje często się spotyka i od dawna jest mowa o tym, że w PHP5 można symulować wielodziedziczenie właśnie w taki sposób.

Mimo wszystko, najlepiej sprawdzić kod przed wysłaniem posta smile.gif
Jarod
Cytat(Ludvik @ 10.08.2006, 21:46 ) *
A sprawdziłeś czy to działa? Na mój gust działa... Tak się składa, że takie konstrukcje często się spotyka i od dawna jest mowa o tym, że w PHP5 można symulować wielodziedziczenie właśnie w taki sposób.

Mimo wszystko, najlepiej sprawdzić kod przed wysłaniem posta smile.gif



lol strzelałem, wyssałem z palca tongue.gif

Po co klasa ma implementować dwa interfejsy? Nie potrafię sobie tego wyobrazić. Tzn. wyobrazić mogę tylko nie łapie sensu ..
Ludvik
Kilka tematów niżej jest przykładowa odpowiedź.
Cysiaczek
Sporo sensu to ma.
np. interfejsy:
1. save(), load()
2. setCommand(), executeCommand()

Juz masz 2 inetrfejsy - uważasz, że można te cztery metody umieścic w jednym intefejsie? Jak go nazwiesz?
Jarod
Cytat(envp @ 10.08.2006, 02:10 ) *
Heh wytlumaczone dobrze, ale dziwnie. smile.gif J4r0d: pojęcie interfejsu znasz prawda? więc wyobraź sobie teraz ze masz 10 klas, ktore dziedzicza ten inerfejs (niech to bedzie metoda foo()) i dla tych klas metoda foo() wyglda tak samo, gdybys uzywal tylko interfesjow musail bys napisac 10 razy ten sam kod,


Zgadza się. Dotąd rozumiem.

Cytat(envp @ 10.08.2006, 02:10 ) *
a tak tworzysz sobie klase abstrakcyjna i ona zawiera juz cale cialo metody foo().


No dobra, czyli masz interfejs:
  1. <?php
  2. interface Metody {
  3. abstract function metoda1();
  4. abstract function metoda2();
  5. }
  6. ?>

i klasę abstrakcyjną:
  1. <?php
  2. abstract class Abstrakcja {
  3. public function foo() {
  4. //... kod metody
  5. }
  6. }
  7. ?>



1. I jak teraz utworzysz klasę, która implementuje interfejs Metody i jednocześnie dziedziczą po klasie Abstrakcja ?

2. Co dokładnie daje dodanie słowa static po słowie function?


Cytat(Apo @ 9.08.2006, 18:56 ) *
(...)
Fajnym przykładem może być obsługa akcji w frameworkach np:
  1. <?php
  2. abstract class SzablonAkcji {
  3.  
  4. protected $przywileje = array('public');
  5.  
  6. abstract public function akcjaAlternatywna(); // wymuszamy utworzenie tej metody w klasach dziedziczących
  7.  
  8. final function OdpalAkcje() // w klasach dziedziczących nie mozemy nadpisac tej metody
  9. {
  10. $action = new HttpRequest->get('action');
  11. $action = $this->$action . '()';
  12.  
  13. if(method_exists($action, $this))
  14. $this->action;
  15. else
  16. $this->akcjaAlternatywna();
  17. }
  18. }?>


Coś jest nie tak w metodzie OdpalAkcje(). Nie powinno być:
  1. <?php
  2. $action = new HttpRequest();
  3. $action = action->get('action');
  4. $action = $this->$action . '()';
  5. ?>

? Nie czaje ale to może dlatego, że to jest kawałek kodu.
Ludvik
W definicji interfejsów nie używamy słowa abstract. Interfejs zawsze definiuje tylko sposób wysłania komunikatu do obiektu.

Dziedzicząc można spokojnie implementować interfejsy.
  1. <?php
  2. class Konkretna extends Abstrakcyjna implements Interfejs1, Interfejs2, Interfejs3 {
  3. // Definicja klasy...
  4. }
  5. ?>


Poczytaj phpedię... Metody statyczne operują na danych statycznych, czyli zajmujących stałe miejsce w pamięci i wywoływanych z kontekstu klasy, a nie obiektu. Metody i właściwości statyczne są dostępne bez konieczności tworzenia obiektów klasy.
Apo
Cytat
2. Co dokładnie daje dodanie słowa static po słowie function?

Nie musisz tworzyć obiektu klasy aby wywołać metode, np singleton. Do metody statycznej odwołujesz się:
NazwaKlasy::metoda();

Cytat
Coś jest nie tak w metodzie OdpalAkcje(). Nie powinno być:
  1. <?php
  2. $action = new HttpRequest();
  3. $action = action->get('action');
  4. $action = $this->$action . '()';
  5. ?>

? Nie czaje ale to może dlatego, że to jest kawałek kodu.


No ja w tym fragmencie popełniłem błąd, teraz jest dobrze.
Jarod
Cytat(Apo @ 12.08.2006, 08:39 ) *
Nie musisz tworzyć obiektu klasy aby wywołać metode, np singleton. Do metody statycznej odwołujesz się:
NazwaKlasy::metoda();


Łapie. Mam jeszcze jedno pytanie. Żeby móc odwoływać się do klasy bez tworzenia obiektu, klasa musi być static.

Ostatnio widziałem gdzieś klasę statyczną, której kilka metod było statycznych a reszta nie. I co wtedy? Bez tworzenia obiektów możemy odwoływać się tylko do metod statycznych? A do pozostałych?
Cysiaczek
A do pozostałych nie laugh.gif - o to właśnie chodzi.Własnie we wzorcu singleton jest pokazana współpraca metod statycznych z niestatycznymi. Do metody statycznej możesz sie odwołac z kontekstu całej aplikacji - wystarczy, ze zadeklarowałeś wcześniej klasę. Jeśli stworzysz obiekt, to już musisz zacząć się o niego martwić tzn. brac poprawki np. na umiejscownienie obiektu smile.gif

Pozdrawiam
dr_bonzo
Nie ma klas statycznych, moga byc tylko metody statyczne ktore wywolujesz:
NazwaKlasy::nazwaMetody()
Jarod
Cytat(Cysiaczek @ 24.08.2006, 19:35 ) *
Jeśli stworzysz obiekt, to już musisz zacząć się o niego martwić tzn. brac poprawki np. na umiejscownienie obiektu smile.gif

Możesz to rozwinąć? Jakie umiejscowienie?
Cysiaczek
Faktycznie - trochę nieściśle się wyraziłem. laugh.gif

Chodzi o zwykły zasięg zmiennych. Jak stworzysz obiekt, to jest on przypisanu do jakiejś zmiennej. Od tego momentu musisz przekazywać jej referencję (lub kopię) do klas, funkcji, etc, jeśli chesz skorzystać z obiektu.

Pozdrawiam.
thornag
@J4r0d Wracajac jeszcze do pytania abstrakcyjne. Pamietasz swoje pytanie dotyczace przekazywania parametrow do metod w stylu addItem(Item $item).

Jasnym jest ze mozesz przekazac tutaj wszystkie instancje class ktore implementuja interfejs Item. Powiedzmy ze ten interfejs wymusza implementacje metody Pokaz() i Schowaj().
A co jesli 4 klasy ktorych chcesz uzywac maja taka sama metode Schowaj() ?
Bedziesz ja przepisywac od nowa ? No nie a tak to mozesz utworzyc abstrakcyjna klase Item w ktorej bedzie ta wpolna metoda w pelni zaimplementowana no i wymuszisz implementacje Pokaz().

Mozna by powiedziec ze po co mi abstrakcja skoro mozna dziedziczyc po zwyklej. No tak ale zwykla klasa nie wymusisz implementacji metody Pokaz() pozatym nie ma sensu tworzyc instancji klasy zawierajacej sama metode Schowaj() wiec tutaj przydaje sie abstrakcja. Nie dosc ze masz zapewnione to ze wszystkie kalsy beda mialy zaimplementowane wymagane przez Ciebie metody to jeszcze korzystasz z zalet dziedziczenia. Swoja droga to sie chyba nazywa polimorfizm winksmiley.jpg to ze klasa wie od jakiej klasy pochodzi i mozna ja przekazac do metody jak wyzej.
Jarod
Cytat(thornag @ 25.08.2006, 14:53 ) *
Jasnym jest ze mozesz przekazac tutaj wszystkie instancje class ktore implementuja interfejs Item. Powiedzmy ze ten interfejs wymusza implementacje metody Pokaz() i Schowaj().
A co jesli 4 klasy ktorych chcesz uzywac maja taka sama metode Schowaj() ?
Bedziesz ja przepisywac od nowa ? No nie a tak to mozesz utworzyc abstrakcyjna klase Item w ktorej bedzie ta wpolna metoda w pelni zaimplementowana no i wymuszisz implementacje Pokaz().

Powoli łapie ale pewnie jeszcze nie raz będę Was gnębił. Jeszcze nie pisze obiektowo (kiedyś troche w C++) ale już dostrzegam zalety programowania obeiktowego. Chcę to zrozumieć a nie pisać dla pisania.

Cytat(thornag @ 25.08.2006, 14:53 ) *
Mozna by powiedziec ze po co mi abstrakcja skoro mozna dziedziczyc po zwyklej. No tak ale zwykla klasa nie wymusisz implementacji metody Pokaz() pozatym nie ma sensu tworzyc instancji klasy zawierajacej sama metode Schowaj() wiec tutaj przydaje sie abstrakcja. Nie dosc ze masz zapewnione to ze wszystkie kalsy beda mialy zaimplementowane wymagane przez Ciebie metody to jeszcze korzystasz z zalet dziedziczenia. Swoja droga to sie chyba nazywa polimorfizm winksmiley.jpg to ze klasa wie od jakiej klasy pochodzi i mozna ja przekazac do metody jak wyzej.


No właśnie można dziedziczyć. Skąd wiesz że tylko metoda Schowaj()? Może byłoby coś więcej winksmiley.jpg Żartuje. Nie łąpie tylko jeszcze dlaczgo wszędzie wszystko wymuszać. WIem, że kilka osób może pisać różne części systemu, ale chyba powinni się najpierw zapoznać z projektem.

Mam jeszcze takie pytania

1. Interfejs zawiera tylko deklaracje metod. Metody te mogą być public (dostępne ze wsząd i dla każdego), private (dostępne tylko dla klasy je zawierającej, niedostępne dla klas które dziedziczą) i protected (dostępne tylko dla klas dziedziczących)? Interfejs gwarantuje, że dana metoda zostanie zaimplementowana w klasie, które implementuje interfejs. To, że klasa interpretuje interfejs nie oznacza, że nie może mieć innych metod?

2. Klasy abstrakcyjne pozwalają na wymuszanie implementacji metod w klasach dziedziczących. Różnią się od interfejsów tym, że mogą zawierać implementację metody. Tworzy się je przez
  1. <?php
  2. abstract class Klasa() {}
  3. ?>

?

Słowo abstract przed metodą klasy wymusza jej implementację w klasie dziedziczącej. Słowo final przed metodą nie pozwala przeciążyć metody w klasie dziedziczącej?

3. Klasa (abstrakcyjna lub konkretna) może zawierać metody statyczne i zwykłe. Do metod statycznych możemy się odwołać bez tworzenia instancji klasy. Metody statyczne tworzy się po to, żeby.. no właśnie po co?
thornag
Wroce do przykladu z ksiazki. Masz jakis mechanizm do otwierania i zamykania. I tak sobie zamykasz i otwierasz jakas klasa otwieraczem.
Otworzyc mozna: worek, plecak, sloik, drzwi.

Worek i sloik a takze reszta nie maja raczej nic wspolnego (no moze worek z plecakiem ale nie o to chodzi), wiec nie mozna zastosowac dziedziczenia bo niby co dziedziczyc ? Z trugiej strony chcesz miec pewnosc ze jak bedziesz uzywac klasy otwieracza to napewno przekazane do niego obiekty beda mialy metody otworz i zamknij. Stad zamiast tworzyc cztery klasy otieracze ktore przyjmuja argumenty na zasadzie addItem1(Worek $worek), addItem2(Plecak $plecak), to tworzysz jedna, ktora w argumencie przyjmuje klasy ktore implementuja interfejs otwierania np. AddItem(Otwieralne $objekt). I teraz masz pewnosci ze niewazne co przekazesz do otwieracza (pod warunkiem ze implementuje wskazany interfejs tu Otwieralne), mozesz wywolac metody zamknij i otworz. Krotki przyklad.

Przypuscmy ze otwieracz jest mala kolekcja zeczy do zamkniecia.
  1. <?php
  2. $otwieracz = New Otwieracz()
  3. $otwieracz->addItem(New Plecak());
  4. $otwieracz->addItem(New Slowik());
  5. $otwieracz->closeAll();
  6. ?>


Teraz dodales do otwieracza trzy instancje klas implementujacych interfejs otwieralne. Mozesz wiec wywolac metode closeAll() ktora iterujac przez obiekty ktore dodales zamknie je wszystkie. Dlaczego taj jest ? Bo w deklaracji metody addItem klasy otwieracz pokazales jaki tym obiektu mozesz przekazac, czyli addItem(Otwieralne $objekt). Jesli sprobujesz inny to zglosi to blad, bo innego przekazac nie mozna. Sens jest tego taki, ze klasy nie musza po sobie dziedziczyc a ty i tak masz pewnosc ze mozesz uzywac na nich metod ktore musza implementowac bo taknakazuje interfejs.

Mam nadzieje ze nie pomotalem Ci za bardzo smile.gif

Po co wszystko wymuszac. Nikt nie mowi ze wszystko, no ale wymuszac to po to po co opisalem wyzej, zeby miec pewnosc ze klasy mozna uzyc w miejscach w ktorych sie chce i nic sie nie wysypie. I po co wszyscy maja sie zapoznawac z kazda klasa w projekcie ? Trzeba by bylo analizowac jej kod i stwoerdzic ze nadaje sie do tego zeby jej obiekt przekazac do otwieracza, lepiej zeby wiedzieli ze implementuje ona(badz nie) interfejs otwieralne, wtedy bez zadnej praktycznie wiedzy, i bez znajomosci metody Otworz() Zamknij() wiedza ze moga ta klase przekazac do Otwieracza. Przeciez taki koder micro%^& nie zna kazdego zakamarka kodu Windowsa smile.gif

1. To ze klasa implementuje interfejs nie oznacza ze nie moze miec innych metod.

2. Tak, jesli zaimplementujesz metode i dodasz do niej final, znaczy to ze nie moze byc ona przeciazona w klasach dziedziczacych. Slowo abstract jak najbardziej oznacza ze ta metode trzeba zaimplementowac w klasie dziedziczacej.

3. Co do metody statycznej. Wyobrazmy sobie cos takiego. Na poczatku skryptu tworzydsz instancje obiektu WebRequest ktory trzyma wszystkie POST GET COOKIE itp i pozwala operowac na nich tak czy inaczej. Teraz zapominasz o WebRequest, tworzysz jakas inna klase ktora gdzies tam w srodku uzywa zmiennych GET/POST czy cos tam. Po co korzystac z nich bezposrednio skoro klasa WebRequest zapewnia nam jakies metody do operacji na nich, i teraz co ? Musimy uzyc klasy WebRequest, tworzymy nowa instancje i uzywamy ? No ale po co ? Przeciez w skrypcie wystarczy nam tylko jedna instancja klasy WebRequest. I co z tym fantem zrobic ? Ano tutaj przychodzi nam na pomoc metoda statyczna. Jako ze nie wiesz czy w momencie w ktorym chcesz sie odwolac do metod WebRequest instancja tej klasy jest juz utworzona czy nie w klasie WebRequest implementujesz statyczna metode getInstance(). Przyklad.

  1. <?php
  2. class WebRequest {
  3.  
  4.  private static $Instance = false;
  5.  
  6. public static function getInstance() {
  7. if(!self::Instance) {
  8.  self::Instance = New WebRequest();
  9. }
  10. return self::Instance;
  11.  }
  12. }
  13. ?>


W ten sposob jesli musimy sie odwolac do metod WebRequest a wiemy ze nie potrzebujemy wiecej niz jednej jej instancji robimy $objekt = WebRequest::getInstance(); w ten sposob jesli instancja WebRequest nie zostala jeszcze utworzona zostanie utworzona, jesli istnieje zostanie zwrocona istniejaca. To jest wzorzec Singleton, dojdziesz do tego w Twojej ksiazce w rozdziale Warstwa Absrakcji dla bazy danych. Od razu mowie zebys nie wierzyl slepo w te przyklady tam bo sa fatalne. Ten sterownik dla PEAR DB to sciema tongue.gif

Jakbym pomotal Ci za bardzo to krzycz smile.gif Jest sobota rano kaca mam smile.gif
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-2024 Invision Power Services, Inc.